drf - 异常源码分析及自定义异常处理

异常部分源码分析

1. 在rest_framework.views的dispath方法中

def dispatch(self, request, *args, **kwargs):
    """
    `.dispatch()` is pretty much the same as Django's regular dispatch,
    but with extra hooks for startup, finalize, and exception handling.
    """
    self.args = args
    self.kwargs = kwargs
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        # 三大验证
        self.initial(request, *args, **kwargs)

        # Get the appropriate handler method
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

        response = handler(request, *args, **kwargs)

    except Exception as exc:
        response = self.handle_exception(exc)  # 捕获万能异常并处理

    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response

2.有异常时,调用 handle_exception 方法

def handle_exception(self, exc):
    """
    Handle any exception that occurs, by returning an appropriate response,
    or re-raising the error.
    """
    # 处理认证失败异常
    if isinstance(exc, (exceptions.NotAuthenticated,
                        exceptions.AuthenticationFailed)):
        # WWW-Authenticate header for 401 responses, else coerce to 403
        auth_header = self.get_authenticate_header(self.request)

        if auth_header:
            exc.auth_header = auth_header
        else:
            exc.status_code = status.HTTP_403_FORBIDDEN
    # 获取异常句柄
    exception_handler = self.get_exception_handler()  # self.settings.EXCEPTION_HANDLER,默认指向 rest_framework.views.exception_handler函数

    context = self.get_exception_handler_context()  # 获取异常处理的内容
    response = exception_handler(exc, context)  # 调用exception_handler方法处理异常
    # 如果response为None,则是exception_handler中未捕获到的异常
    if response is None:
        self.raise_uncaught_exception(exc)

    response.exception = True
    return response

3.调用 exception_handler 方法处理异常

def exception_handler(exc, context):
    # Http404异常处理
    if isinstance(exc, Http404):
        exc = exceptions.NotFound()
    # PermissionDenied权限异常处理
    elif isinstance(exc, PermissionDenied):
        exc = exceptions.PermissionDenied()
    # APIException异常处理
    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)
    # 未捕获到的异常,返回None
    return None

自定义异常处理

可见如果需要自定义异常处理,只需要重写exception_handler 方法。将exception_handler指向我们所写的 exception_handler 方法

setting.py文件添加配置

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'apis.exceptions.exception_handler',
}

exceptions.py文件重写exception_handler方法

from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
def exception_handler(exc,context):
    # drf的异常还是提交给 drf_exception_handler,我们只需要处理drf未处理的异常
    response = drf_exception_handler(exc,context)

    if response is None:
        # drf 处理不了的异常
        # 可以在此通过 logging 记录异常日志
        return Response({
            'exception':'服务器异常'
        }, status=500)

    response.exception = True
    return response
posted @ 2019-09-15 19:59  Never&say&die  阅读(214)  评论(0编辑  收藏  举报