首页 文章

Django Rest Framework中的ValidationError JWT不使用自定义异常处理程序

提问于
浏览
0

我正在使用Django Rest Framework 3.2.3(DRF)和Django Rest Framework JWT 1.7.2(DRF-JWT,https://github.com/GetBlimp/django-rest-framework-jwt)来创建Login令牌 .

在发布从400到202的JWT时,我需要更改无效凭据的状态代码(仅供参考:我的客户端无法读取非200响应的正文) . 我使用Django Rest Framework描述的自定义异常处理程序来实现它:http://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling

restapi/custom_exception.py

from rest_framework.views import exception_handler
from rest_framework import status

def custom_exception_handler(exc, context):
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc, context)

    print ('Exception raised: ' + str(response.status_code))

    # Now add the HTTP status code to the response.
    if response is not None:
        if response.status_code != status.HTTP_403_FORBIDDEN:
            response.status_code = status.HTTP_202_ACCEPTED

    return response

并在配置中:

'EXCEPTION_HANDLER': 'restapi.custom_exception.custom_exception_handler',

当使用无效凭证时,DRF-JWT应引发ValidationError . 在向JWT token-auth接口发布无效凭据时,我仍然收到400 Bad Request响应代码 .

对于每个其他DRF接口,我按预期获得202状态代码 .

How can I get DRF-JWT to use the custom exception handler for their ValidationErrors?

2 回答

  • 3

    Why we are not able to use custom exception handler here?

    发生这种情况是因为 raise_exception 标志在调用 .is_valid() 时尚未传递给 JSONWebTokenSerializer . ( JSONWebTokenSerializer 是用于验证用户名和密码的序列化程序类 . )

    DRF-JWT source code for post() method:

    def post(self, request):
        serializer = self.get_serializer(
            data=get_request_data(request)
        )
    
        if serializer.is_valid(): # 'raise_exception' flag has not been passed here
            user = serializer.object.get('user') or request.user
            token = serializer.object.get('token')
            response_data = jwt_response_payload_handler(token, user, request)
    
            return Response(response_data)
    
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    

    现在,我们可以看到 raise_exception 标志尚未传递给 is_valid() . 发生这种情况时,不会引发 ValidationError ,从而导致 custom_exception_handler 代码无法执行 .

    根据Raising an exception on invalid data:上的DRF部分

    .is_valid()方法采用可选的raise_exception标志,如果存在验证错误,则会导致它引发serializers.ValidationError异常 . 这些异常由REST框架提供的默认异常处理程序自动处理,默认情况下将返回HTTP 400 Bad Request响应 .

    SOLUTION:

    如果在调用 .is_valid() 函数时将 raise_exception 标志传递为 True ,则将执行 custom_exception_handler 的代码 .

    您将需要创建一个将从默认 ObtainJSONWebToken 视图继承的 CustomObtainJSONWebToken 视图 . 在这里,我们将覆盖 .post() 方法以传递 raise_exception 标志 . 然后将在我们的网址中指定此视图 .

    my_app/views.py

    from rest_framework_jwt.views import ObtainJSONWebToken
    
    class CustomObtainJSONWebToken(ObtainJSONWebToken):
    
        def post(self, request):
            serializer = self.get_serializer(
                data=get_request_data(request)
            )
    
            serializer.is_valid(raise_exception=True) # pass the 'raise_exception' flag
            user = serializer.object.get('user') or request.user
            token = serializer.object.get('token')
            response_data = jwt_response_payload_handler(token, user, request)
            return Response(response_data)
    

    urls.py

    # use this view to obtain token
    url(r'^api-token-auth/', CustomObtainJSONWebToken.as_view())
    
  • 0

    我认为可能更简洁的做法,不涉及覆盖post方法

    serializers.py

    from rest_framework_jwt import serializers as jwt_serializers
    
    class JSONWebTokenSerializer(jwt_serializers.JSONWebTokenSerializer):
        """
        Override rest_framework_jwt's ObtainJSONWebToken serializer to 
        force it to raise ValidationError exception if validation fails.
        """
        def is_valid(self, raise_exception=None):
            """
            If raise_exception is unset, set it to True by default
            """
            return super().is_valid(
                raise_exception=raise_exception if raise_exception is not 
    None else True)
    

    views.py

    from rest_framework_jwt import views as jwt_views
    from .serializers import JSONWebTokenSerializer
    
    class ObtainJSONWebToken(jwt_views.ObtainJSONWebToken):
        """
        Override the default JWT ObtainJSONWebToken view to use the custom serializer
        """
        serializer_class = JSONWebTokenSerializer
    

    urls.py

    from django.conf.urls import url
    from .views import ObtainJSONWebToken
    
    urlpatterns = [
        ...
        url(r'^api-token-auth/', ObtainJSONWebToken.as_view(), name='jwt-create'),
    ]
    

相关问题