Ricardo

DRF 异常处理与自定义渲染笔记

阅读时长: 3 分钟

django restful 异常处理

相关链接

标准restful异常处理

REST framework提供了异常处理,我们可以自定义异常处理函数。

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    # 先调用REST framework默认的异常处理方法获得标准错误响应对象
    response = exception_handler(exc, context)

    # 在此处补充自定义的异常处理
    if response is not None:
        response.data['status_code'] = response.status_code

    return response

在配置文件中声明自定义的异常处理

Python
1
2
3
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}

如果未声明,会采用默认的方式,如下

Python
1
2
3
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}

数据库异常处理案例

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError

def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if response is None:
        view = context['view']
        if isinstance(exc, DatabaseError):
            print('[%s]: %s' % (view, exc))
            response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response

REST framework定义的异常 APIException 所有异常的父类 ParseError 解析错误 AuthenticationFailed 认证失败 NotAuthenticated 尚未认证 PermissionDenied 权限决绝 NotFound 未找到 MethodNotAllowed 请求方式不支持 NotAcceptable 要获取的数据格式不支持 Throttled 超过限流次数 ValidationError 校验失败

如果是默认的Exception,就不会被restful的Exception捕获

例如下面的

代码
1
AttributeError: 'AnonymousUser' object has no attribute 'nickname' 

这种属实python默认的异常,不会被Django框架捕获,这种异常需要自己处理返回内容。

如果继承了restful的api,函数处理异常

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from rest_framework.views import APIView

class UserInfoView(APIView):
    """
    获取当前用户信息
    """

    def get(self, request):
        data = {
            'id': request.user.id,
            'username': request.user.username,
            'nickname': request.user.nickname,
            'email': request.user.email,
            'is_active': request.user.is_active,
            'roles': [role["name"] for role in request.user.roles.values()]
        }
        logger.info(f'获取用户信息成功, {request.user.username}')
        return Response(data=data)

遇到异常就会走restful的框架的异常处理,返回json数据

代码
1
2
3
{
    "msg": "服务器错误:'AnonymousUser' object has no attribute 'nickname'"
}

restful 的异常处理不是内部的exception,而是已知的异常优化展示。

Note that the exception handler will only be called for responses generated by raised exceptions. It will not be used for any responses returned directly by the view, such as the HTTP_400_BAD_REQUEST responses that are returned by the generic views when serializer validation fails.

请注意,仅对引发的异常生成的响应调用异常处理程序。它不会用于视图直接返回的任何响应,例如,当序列化程序验证失败时,泛型视图返回的 HTTP_400_BAD_REQUEST 响应。

第三方的模块处理异常

如果第三方模块也做了异常处理展示,会被 exception_handler 包裹,只会在通用异常里

InvalidToken({‘detail’: ‘Given token not valid for any token type’, ‘messages’: [{’token_class’: ‘AccessToken’, ’token_type’: ‘access’, ‘message’: ‘Token is invalid or expired’}]})

使用 exception_handler 处理后的展示

代码
1
2
3
4
{
    "code": 400001,
    "msg": "服务器错误:{'detail': ErrorDetail(string='Given token not valid for any token type', code='token_not_valid'), 'code': ErrorDetail(string='token_not_valid', code='token_not_valid'), 'messages': [{'token_class': ErrorDetail(string='AccessToken', code='token_not_valid'), 'token_type': ErrorDetail(string='access', code='token_not_valid'), 'message': ErrorDetail(string='Token is invalid or expired', code='token_not_valid')}]}"
}

走过 CustomRenderer

经过 EXCEPTION_HANDLER 后还会经过 DEFAULT_RENDERER_CLASSES

这个是有点意外的,那就在 DEFAULT_RENDERER_CLASSES 通配处理渲染


Python
1
2
3
    def render(self, data, accepted_media_type=None, renderer_context=None):
        print('renderer_context => ', renderer_context)
        # response = {

哪里调用了这个render

这里的data

Python
1
{'msg': "服务器错误:{'detail': ErrorDetail(string='Given token not valid for any token type', code='token_not_valid'), 'code': ErrorDetail(string='token_not_valid', code='token_not_valid'), 'messages': [{'token_class': ErrorDetail(string='AccessToken', code='token_not_valid'), 'token_type': ErrorDetail(string='access', code='token_not_valid'), 'message': ErrorDetail(string='Token is invalid or expired', code='token_not_valid')}]}", 'data': {'detail': ErrorDetail(string='Given token not valid for any token type', code='token_not_valid'), 'code': ErrorDetail(string='token_not_valid', code='token_not_valid'), 'messages': [{'token_class': ErrorDetail(string='AccessToken', code='token_not_valid'), 'token_type': ErrorDetail(string='access', code='token_not_valid'), 'message': ErrorDetail(string='Token is invalid or expired', code='token_not_valid')}]}}

从里面没有太哦

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
记录自己
Built with Hugo
主题 Stack 由 Jimmy 设计