自定义响应渲染器


如果你采用方法二(自定义响应渲染器),并且需要自定义返回的 code 字段(区别于 HTTP 状态码),可以通过以下方式实现:

方案一:在视图中设置自定义状态码

在视图方法中通过 response.data 添加自定义状态码,渲染器读取该值:

# 视图中
class TimeMoudelSet(viewsets.ModelViewSet):
    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset, many=True)
        
        # 自定义业务状态码(非HTTP状态码)
        response = Response(serializer.data)
        response.data = {
            "data": serializer.data,
            "custom_code": 2000  # 自定义业务状态码
        }
        return response

修改渲染器代码:

# utils/renderers.py
from rest_framework.renderers import JSONRenderer

class CustomJSONRenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        response = renderer_context['response']
        http_status = response.status_code
        
        # 默认业务状态码(与HTTP状态码映射)
        business_code = {
            200: 2000,  # 成功
            201: 2001,  # 创建成功
            400: 4000,  # 参数错误
            401: 4001,  # 未认证
            403: 4003,  # 权限拒绝
            404: 4004,  # 资源不存在
            500: 5000,  # 服务器错误
        }.get(http_status, 5000)
        
        # 如果视图中设置了custom_code,则使用它
        if isinstance(data, dict) and 'custom_code' in data:
            business_code = data.pop('custom_code')  # 移除自定义code,避免嵌套
        
        # 错误处理
        if isinstance(data, dict) and 'detail' in data:
            return super().render({
                "data": None,
                "code": business_code,
                "message": data['detail']
            }, accepted_media_type, renderer_context)
        
        # 成功响应
        return super().render({
            "data": data,
            "code": business_code
        }, accepted_media_type, renderer_context)

方案二:使用自定义异常处理

定义业务异常类,在抛出异常时携带自定义状态码:

# utils/exceptions.py
from rest_framework.exceptions import APIException

class CustomAPIException(APIException):
    status_code = 400  # HTTP状态码
    default_code = 4000  # 业务状态码
    
    def __init__(self, detail=None, code=None):
        self.code = code or self.default_code
        super().__init__(detail=detail)

修改渲染器:

# utils/renderers.py
from rest_framework.renderers import JSONRenderer
from utils.exceptions import CustomAPIException

class CustomJSONRenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        response = renderer_context['response']
        http_status = response.status_code
        
        # 默认业务状态码
        business_code = {
            200: 2000,
            201: 2001,
            # ...其他映射
        }.get(http_status, 5000)
        
        # 如果是自定义异常,获取其业务状态码
        exception = getattr(response, 'exception', False)
        if exception and isinstance(getattr(response, 'data', None), dict):
            # 从异常中获取业务状态码(如果有)
            if hasattr(response.exception, 'code'):
                business_code = response.exception.code
        
        # 错误处理
        if isinstance(data, dict) and 'detail' in data:
            return super().render({
                "data": None,
                "code": business_code,
                "message": data['detail']
            }, accepted_media_type, renderer_context)
        
        # 成功响应
        return super().render({
            "data": data,
            "code": business_code
        }, accepted_media_type, renderer_context)

使用示例:

# 在视图中抛出异常
from utils.exceptions import CustomAPIException

def some_view(self, request):
    if not request.user.is_authenticated:
        raise CustomAPIException("未登录", code=4001)

方案三:全局状态码映射配置

settings.py 中配置 HTTP 状态码与业务状态码的映射关系:

# settings.py
CUSTOM_STATUS_CODE_MAPPING = {
    200: 2000,
    201: 2001,
    400: 4000,
    401: 4001,
    # ...其他映射
}

渲染器读取配置:

# utils/renderers.py
from rest_framework.renderers import JSONRenderer
from django.conf import settings

class CustomJSONRenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        response = renderer_context['response']
        http_status = response.status_code
        
        # 从配置中获取业务状态码映射
        code_mapping = getattr(settings, 'CUSTOM_STATUS_CODE_MAPPING', {})
        business_code = code_mapping.get(http_status, 5000)
        
        # ...后续代码同之前方案

推荐方案(方案一)

对于你的场景,最简单的方式是在视图中直接设置 custom_code

class TimeMoudelSet(viewsets.ModelViewSet):
    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset, many=True)
        
        response = Response(serializer.data)
        response.data = {
            "data": serializer.data,
            "custom_code": 2000  # 业务成功状态码
        }
        return response

渲染器代码保持不变,自动读取并使用 custom_code 作为返回的 code 字段。这样可以灵活控制每个接口的业务状态码。

声明:一代明君的小屋|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 自定义响应渲染器


欢迎来到我的小屋