全局异常处理

一. 常见类型(DRF 的异常)

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

二. 异常处理

1. 代码示例

创建一个 py 文件 例如:exceptions.py

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response

# 重写 exception_handler 函数
def common_exception_handler(exc, context):
    """
        # 视图类的对象
        view = context['view']

        # 当前请求的对象, ip,用户id,当前时间,请求地址来
        request = context['request']

        print('ip地址为:%s的用户,访问:%s 视图类,报错了,请求地址是:%s'%(request.META.get('REMOTE_ADDR'),str(view),request.path))
    """

    # 在这里可以记录日志
    # 日志记录,越详细越好:哪个用户(id,ip),在什么时间,执行哪个视图函数时报了错,请求地址是什么
    logger.error(f'用户:{user_id}, 请求方法:{request.method}, 请求路径:{request.path}, 视图函数: {view}, 错误:{exc}')

    # """ ------------------以下为主要代码---------------------- """"

    # res 有值则是 drf 异常, 值为 None 则是 django 异常
    res = drf_exception_handler(exc, context)
    # 在这里,可以通过状态码,把异常分的更细一些: 比如-----> 有数据的异常,除以0的异常,列表越界异常。.


    if res:
        # 这是drf的异常,其实人家已经处理了,但是不符合我的格式 。
        res = Response({'code':999,'msg':response.data.get('detail', "服务器出错,请联系系统管理员")})

    else:
        res = Response({'code':998,'msg':str(exc)})

    # 一定不要忘记 return
    return res

2. 注册使用

REST_FRAMEWORK = {
    # 自己写的全局异常捕获 《 值为文件路径 》
    'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler',
}

3. 源码刨析

(1). APIView 执行异常处理的过程

# 1. 导入 APIView
    from rest_framework.views import APIView

# 2. 发现 APIView 源码中有一个异常处理函数 handle_exception 代码如下:《只看注释部分》
    def handle_exception(self, exc):
        if isinstance(exc, (exceptions.NotAuthenticated, exceptions.AuthenticationFailed)):
            auth_header = self.get_authenticate_header(self.request)
            if auth_header:
                exc.auth_header = auth_header
            else:
                exc.status_code = status.HTTP_403_FORBIDDEN

        # 在这里获取异常处理函数,见下面代码 3
        exception_handler = self.get_exception_handler()

        context = self.get_exception_handler_context()

        # 在这里执行异常处理函数
        response = exception_handler(exc, context)

        if response is None:
            # 如果没有配置异常处理就直接抛出 exc 见代码 4
            self.raise_uncaught_exception(exc)

        response.exception = True
        return response


# 3. get_exception_handler函数,还回了配置文件中的配置项 《就是自己写的异常处理函数,或 DRF 的》
    def get_exception_handler(self):
        return self.settings.EXCEPTION_HANDLER

# 4.
    def raise_uncaught_exception(self, exc):
        if settings.DEBUG:
            request = self.request
            renderer_format = getattr(request.accepted_renderer, 'format')
            use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin')
            request.force_plaintext_errors(use_plaintext_traceback)
        raise exc

(2). DRF 的异常处理

# 导入
from rest_framework.views import exception_handler

"""以下是 DRF 源码部分对异常的处理"""

# DRF 的异常处理
def exception_handler(exc, context):

    if isinstance(exc, Http404):
        exc = exceptions.NotFound(*(exc.args))
    elif isinstance(exc, PermissionDenied):
        exc = exceptions.PermissionDenied(*(exc.args))

    if isinstance(exc, exceptions.APIException):
        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 isinstance(exc.detail, (list, dict)):
            data = exc.detail
        else:
            data = {'detail': exc.detail}

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

    return None


# -----------------------一下是源码的解释-------------------------

# 返回应用于任何给定异常的响应。
Returns the response that should be used for any given exception.

# 默认情况下,我们处理REST框架的APIException,和
# Django内置的' Http404 '和' PermissionDenied '异常
By default we handle the REST framework ‘APIException’, and also
Django’s built-in 'Http404' and 'PermissionDenied' exceptions。

# 任何未处理的异常都可能返回 'None',这将导致引发500错误
Any unhandled exceptions may return `None`, which will cause a 500 error to be raised.

三. 自定义异常

class MyCustomException(Exception):
    def __init__(self, message):
        self.message = message

    def __str__(self):
        return f"MyCustomException: {self.message}"


# 使用自定义异常
try:
    raise MyCustomException("This is an error message")
except MyCustomException as e:
    print(e) # 结果:MyCustomException: This is an error message
posted @ 2023-04-18 13:35  codegjj  阅读(16)  评论(0编辑  收藏  举报