Django-djangorestframework-异常模块-源码及自定义异常

目录

  • 异常模块

    • 为什么要自定义异常模块
    • 常见的几种异常情况
    • 异常模块源码分析
    • 自定义 drf 异常处理

异常模块

为什么要自定义异常模块

  1. 所有经过 drf APIView 视图类产生的异常,都可以提供异常处理方案(没有继承 APIVIew 的视图函数不会触发)
  2. drf 默认提供了异常处理方案(rest_framework.views.exception_handler),但是处理范围有限
  3. drf 提供的处理方案有两种
    • 有对应处理,处理了返回异常信息
    • 没有对应处理(处理范围之外),返回 None,直接服务器抛异常给前台
  4. 自定义异常的目的就是解决 drf 没有处理的异常,让前台得到合理的异常信息返回,后台记录异常具体的信息(方便事后排查)

如果程序报错了,我们应该尽可能的隐藏后台的错误,返回给前台就是服务器错误(你返回给用户用户也看不懂呀,如果是黑客,那可能还会利用报错袭击服务器)

常见的几种异常情况

  1. 像这种就比较可怕了,甚至连代码文件位置都暴露了

  1. drf 异常处理模块处理后的异常

  1. drf 异常处理模块处理后的异常

  1. 异常信息经汉化后的报错(django 配置了国际化后)

异常模块源码分析

视图函数执行出现异常会自动触发 handle_exception 函数

每个请求都会经历这么一个过程,走到 dispatch 函数

E:/python3-6-4/Lib/site-packages/rest_framework/views.py 源码

    # ...
    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)  # 上面 try 代码体内代码出现异常会自动触发这个函数 <---------

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

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()  # 获取处理异常的句柄(方法) <---------

        context = self.get_exception_handler_context()
        # 异常处理的结果
        # 自定义异常就是提供 exception_handler 异常处理函数,处理的目的就是让 response 一定有值
        response = exception_handler(exc, context)

        if response is None:
            self.raise_uncaught_exception(exc)  # 乱七八糟的异常就是这里抛出来的

        response.exception = True
        return response

如何获取异常类?

get_exception_handler_context 源码,异常处理类是从配置中拿来的

    def get_exception_handler(self):
        """
        Returns the exception handler that this view uses.
        """
        return self.settings.EXCEPTION_HANDLER

    # API policy implementation methods

E:/python3-6-4/Lib/site-packages/rest_framework/settings.py

    # Exception handling
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',

获取到异常类如何处理?

返回 None 就会触发 handle_exception 源码中的报错

E:/python3-6-4/Lib/site-packages/rest_framework/views.py drf 自带的异常处理类

def exception_handler(exc, context):
    """
    Returns the response that should be used for any given exception.

    By default we handle the REST framework `APIException`, and also
    Django's built-in `Http404` and `PermissionDenied` exceptions.

    Any unhandled exceptions may return `None`, which will cause a 500 error
    to be raised.
    """
    if isinstance(exc, Http404):
        exc = exceptions.NotFound()
    elif isinstance(exc, PermissionDenied):
        exc = exceptions.PermissionDenied()

    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  # 其他的异常 drf 未处理,返回 None,让其报错(最上面的那种报错)

自定义 drf 异常处理

自定义异常处理模块就是提供 exception_handler 异常处理函数,处理的目的就是让 response 一定有值

显而易见,我们只需要自定义一个异常处理方法,先调用系统自带的那个异常处理函数,然后把 drf 自带那个异常函数没有处理的情况处理了就好了(处理后返回一个 Response 对象即可,一定要有返回值,否则没多大意义)

歩鄹

  1. 先将异常处理交给 rest_framework.views 的 exception_handler 去处理
  2. 判断处理的结果(返回值)response,有值代表 drf 已经处理了,None 需要自己处理
    • 可以根据 exc 的类型再细化处理 if isinstance(exc, ‘哪个异常‘): # 再怎么处理

api/exception.py

记得自己把报错信息记到日志里面去

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

def exception_handler(exc, context):
    # drf的exception_handler做基础处理
    response = drf_exception_handler(exc, context)
    # 为空,说明 drf 中没有对应的处理,咱们自定义二次处理
    if response is None:
        # print(exc)
        # # Book matching query does not exist

        # print(context)
        # # {'view': <api.views.Book object at 0x000001FED29DD860>}, 'args': (), 'kwargs': {'pk': '4'}, 'request': <rest_framework.request.Request object at 0x000001FED2CD9EF0>

        # 这里后期应该写成系统日志才对(这只是演示的伪代码)
        print('%s - %s - %s' % (context['view'], context['request'].method, exc))
        # <api.views.Book object at 0x000002505A2A9A90> - GET - Book matching query does not exits.
        return Response({
            'detail': '服务器错误'
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
    return response

配置上,让其生效

dg_proj/settings.py

# 1.确保已注册 drf
INSTALLED_APPS = [
    # ...
    'api.apps.ApiConfig',

    'rest_framework',  # 注册 drf
]

# 2.在 restframework 的配置中配置该自定义异常模块
REST_FRAMEWORK = {
    # ...

    'EXCEPTION_HANDLER': 'api.exception.exception_handler',  # 全局配置异常模块
}

原文地址:https://www.cnblogs.com/suwanbin/p/12013311.html

时间: 2024-10-15 02:08:28

Django-djangorestframework-异常模块-源码及自定义异常的相关文章

分布式事务中间件 Fescar—RM 模块源码解读

前言在SOA.微服务架构流行的年代,许多复杂业务上需要支持多资源占用场景,而在分布式系统中因为某个资源不足而导致其它资源占用回滚的系统设计一直是个难点.我所在的团队也遇到了这个问题,为解决这个问题上,团队采用的是阿里开源的分布式中间件Fescar的解决方案,并详细了解了Fescar内部的工作原理,解决在使用Fescar中间件过程中的一些疑虑的地方,也为后续团队在继续使用该中间件奠定理论基础. 目前分布式事务解决方案基本是围绕两阶段提交模式来设计的,按对业务是有侵入分为:对业务无侵入的基于XA协议

【nodejs原理&amp;源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)

目录 一. 概述 二. 线程与进程 三. cluster模块源码解析 3.1 起步 3.2 入口 3.3 主进程模块master.js 3.4 子进程模块child.js 四. 小结 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 华为云社区地址:[你要的前端打怪升级指南] 一. 概述 cluster模块是node.js中用于实现和管理多进程的模块.常规的node.js应用程序是单线程单进程的,这也意味

ngx lua模块源码简单解析

ngx lua模块源码简单解析分类: nginx 2014-07-11 11:45 2097人阅读 评论(0) 收藏 举报nginxlua数据结构架构目录(?)[+]对nginx lua模块的整个流程,原理简单解析.由于nginx lua模块相关配置,指令,API非常多,所以本文档只以content_by_lua指令举例说明. 读本文档最好配合读源码. 不适合对nginx和lua一点都不了解的人看.1.相关配置详细配置见 https://github.com/openresty/lua-ngin

XposedNoRebootModuleSample 不需要频繁重启调试的Xposed 模块源码例子

XposedNoRebootModuleSample(不需要频繁重启调试的Xposed 模块源码例子) Xposed Module Sample No Need To Reboot When Debug github:https://github.com/asiontang/XposedNoRebootModuleSample @Override public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPac

基于Python的datetime模块和time模块源码阅读分析

目录 1 前言  2 datetime.pyi源码分步解析 2.1 头部定义源码分析 2.2 tzinfo类源码分析 2.3 date类源码分析 2.4 time类源码分析 2.5 timedelta类源码分析 2.6 datetime类源码分析 2.7 格式化字符串 3 time模块time.pyi源码解析 1 前言 最近工作需求上对于datetime模块中的方法调用比较多,有时还要返回指定的格式,以及大小比较等情况.发现使用Python自带的datetime模块可以很好地实现相关需求,但是对

koa2--delegates模块源码解读

delegates模块是由TJ大神写的,该模块的作用是将内部对象上的变量或函数委托到外部对象上.然后我们就可以使用外部对象就能获取内部对象上的变量或函数.delegates委托方式有如下: getter: 外部对象可以通过该方法访问内部对象的值.setter:外部对象可以通过该方法设置内部对象的值.access: 该方法包含getter和setter功能.method: 该方法可以使外部对象直接调用内部对象的函数. 项目文件如下结构: |----- 项目 | |-- delegates.js #

python附录-re.py模块源码(含re官方文档链接)

re模块 python官方文档链接:https://docs.python.org/zh-cn/3/library/re.html re模块源码 r"""Support for regular expressions (RE). This module provides regular expression matching operations similar to those found in Perl. It supports both 8-bit and Unicod

DRF ---- APIview生命周期 请求/渲染/解析/异常/响应/ 模块源码 drf配置

目录 drf框架的封装风格 1. 原生Django View的源码复习 as_view源码 dispatch源码 2. ApiView的生命周期(源码) 重写的as_view源码 重写的dispatch源码 3 . 请求模块 initialize_request 源码 Request 源码 Request 下 __getattr_ 源码 总结(重点) 4. 渲染模块(了解) 5. Drf配置(重点) drf APISettings 默认配置 drf框架 自定义 全局配置 drf框架 自定义 局部

shiro登录模块源码分析

接触和使用shiro还是有好大一段时间,可惜并没有搞明白其实的原理和机制.因为工作中使用的框架都封装好了,so......并没有去研究. 原来一直猜想的是shiro可以直接连接数据库,验证用户名和密码.但是又没在实体类中找到有什么特殊的标记注解之类的. 就有点好奇了,于是趁这两天工作不是那么紧张了,就跟了下源码. 如有不对,请留言指正.蟹蟹! 红色部分是主要代码: UsernamePasswordToken token = new UsernamePasswordToken(user.getUs