问题描述
我正在使用带有简单 JWT 的 Django REST 框架,我不喜欢权限类如何生成响应而不给我发送给客户端的最终决定权。使用其他不限制权限(登录、注册等)的基于类的视图,我可以控制处理异常的方式,并且可以选择响应数据的结构。
然而,每当我引入权限类时,都会出现不希望的行为。我想要的结构最好在我的 LoginView 中表示(参见 try/except 块):
NON_FIELD_ERRORS_KEY = settings.REST_FRAMEWORK['NON_FIELD_ERRORS_KEY']
class LoginView(GenericAPIView):
"""
View for taking in an existing user's credentials and authorizing them if valid or denying access if invalid.
"""
serializer_class = LoginSerializer
def post(self,request):
"""
POST method for taking a token from a query string,checking if it is valid,and logging in the user if valid,or returning an error response if invalid.
"""
serializer = self.serializer_class(data=request.data)
try:
serializer.is_valid(raise_exception=True)
except AuthenticationFailed as e:
return Response({NON_FIELD_ERRORS_KEY: [e.detail]},status=e.status_code)
return Response(serializer.data,status=status.HTTP_200_OK)
但是,当我尝试使用权限类定义视图时会发生什么?
class logoutView(GenericAPIView):
"""
View for taking in a user's access token and logging them out.
"""
serializer_class = logoutSerializer
permission_classes = [IsAuthenticated]
def post(self,request):
"""
POST method for taking a token from a request body,and logging out the user if valid,or returning an error response if invalid.
"""
access = request.Meta.get('HTTP_AUTHORIZATION','')
serializer = self.serializer_class(data={'access': access})
try:
serializer.is_valid(raise_exception=True)
except AuthenticationFailed as e:
return Response({NON_FIELD_ERRORS_KEY: [e.detail]},status=e.status_code)
return Response(serializer.save(),status=status.HTTP_205_RESET_CONTENT)
当我去测试这个时,带有无效授权标头的请求在 post()
的范围之外被处理,所以执行根本不会到达方法。相反,我被迫处理与我的项目的其余部分不一致的响应。举个例子:
# Desired output
{
'errors': [
ErrorDetail(string='Given token not valid for any token type',code='token_not_valid')
]
}
# Actual output
{
'detail': ErrorDetail(string='Given token not valid for any token type',code='token_not_valid'),'code': ErrorDetail(string='token_not_valid','messages': [
{
'token_class': ErrorDetail(string='Accesstoken','token_type': ErrorDetail(string='access','message': ErrorDetail(string='Token is invalid or expired',code='token_not_valid')
}
]
}
是否有一种简单的方法可以更改这些响应的格式?
解决方法
在单步执行代码后,我发现APIView.initial
的最后一个注释部分引发了我所关注的异常。
# rest_framework/views.py (lines 399-416)
def initial(self,request,*args,**kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer,request.accepted_media_type = neg
# Determine the API version,if versioning is in use.
version,scheme = self.determine_version(request,**kwargs)
request.version,request.versioning_scheme = version,scheme
# Ensure that the incoming request is permitted
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
要生成自定义响应,请使用重载的 APIView
方法创建 initial()
的自定义实现,或者在 APIView
的特定实例(或后代,例如 {{ 1}}).
GenericAPIView