自定义Django Rest Framework序列化器的状态码响应

4
场景非常简单:假设我有一个用户模型,其中电子邮件应该是唯一的。我为此编写了自定义验证,如下所示:

def validate_email(self, value):
    if value is not None:
        exist_email = User.objects.filter(email=value).first()
        if exist_email:
            raise serializers.ValidationError("This Email is already taken")
    return value

当输入验证出现问题时,我们应该从rest_framework响应中返回状态码400表示BAD_REQUEST,但在这种情况下,我们应该或者需要返回状态码409表示冲突项。如何最好地定制序列化器错误验证的状态码响应。

3个回答

2
我建议拦截ValidationError异常并返回状态码为409的响应对象:
try:
    serializer.is_valid(raise_exception=True)
except ValidationError, msg:
    if str(msg) == "This Email is already taken":
        return Response(
            {'ValidationError': str(msg)},
            status=status.HTTP_409_CONFLICT
        )
    return Response(
        {'ValidationError': str(msg)},
        status=status.HTTP_400_BAD_REQUEST
    )

2

我认为最好定义自定义的异常处理程序,例如:

settings.py

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'myproject.common.custom_classes.handler.exception_handler',
}

handler.py

def exception_handler(exc, context):
    # Custom exception hanfling
    if isinstance(exc, UniqueEmailException):
        set_rollback()
        data = {'detail': exc.detail}
        return Response(data, status=exc.status_code)

    elif isinstance(exc, (exceptions.APIException, ValidationError)):
        headers = {}
        if getattr(exc, 'auth_header', None):
            headers['WWW-Authenticate'] = exc.auth_header
        if getattr(exc, 'wait', None):
            headers['Retry-After'] = '%d' % exc.wait

        if hasattr(exc, 'error_dict') and isinstance(exc, ValidationError):
            exc.status_code = HTTP_400_BAD_REQUEST
            data = exc.message_dict
        elif isinstance(exc.detail, (list, dict)):
            data = exc.detail
        else:
            data = {'detail': exc.detail}

        set_rollback()
        return Response(data, status=exc.status_code, headers=headers)

    elif isinstance(exc, Http404):
        msg = _('Not found.')
        data = {'detail': six.text_type(msg)}

        set_rollback()
        return Response(data, status=status.HTTP_404_NOT_FOUND)

    return None

exceptions.py

class UniqueEmailException(APIException):
    status_code = status.HTTP_409_CONFLICT
    default_detail = 'Error Message'

最后是验证器:

def validate_email(self, value):
    if value is not None:
        exist_email = User.objects.filter(email=value).first()
        if exist_email:
            raise UniqueEmailException()
    return value

2

简短回答:

在序列化器中,无法返回自定义响应代码。

这是因为序列化器只是一个序列化器。它不应该处理HTTP。它只是用于格式化数据,通常是JSON,但通常会为显示API生成HTML和其他一两个格式。

详细回答:

一种解决方法是在序列化器中引发异常(无论是什么,但要使其描述清楚),并在视图中添加代码以捕获该错误。您的视图可以根据需要返回自定义响应代码和自定义响应正文。

就像这样:

在视图类中添加类似以下内容:

def create(self, request, *args, **kwargs):
   try:
       return super().create(request, *args, **kwargs)
   except ValidationError as x:
       return Response(x.args, status=status.HTTP_409_CONFLICT)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接