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

时间:2015-09-22 09:22:32

标签: python django django-rest-framework jwt

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

我需要在从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状态代码。

如何让DRF-JWT为其ValidationErrors使用自定义异常处理程序?

2 个答案:

答案 0 :(得分:3)

为什么我们无法在此处使用自定义异常处理程序?

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

post()方法的DRF-JWT源代码:

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   默认情况下请求回复。

<强> SOLUTION:

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

您需要创建一个CustomObtainJSONWebToken视图,该视图将从默认的ObtainJSONWebToken视图继承。在此,我们将覆盖.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()) 

答案 1 :(得分: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'),
]