异常模块

为什么要自定义异常模块

  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', # 全局配置异常模块
}

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

  1. Django(50)drf异常模块源码分析

    异常模块源码入口 APIView类中dispatch方法中的:response = self.handle_exception(exc) 源码分析 我们点击handle_exception跳转,查看该 ...

  2. Django(49)drf解析模块源码分析

    前言 上一篇分析了请求模块的源码,如下: def initialize_request(self, request, *args, **kwargs): """ Retu ...

  3. Django(51)drf渲染模块源码分析

    前言 渲染模块的原理和解析模块是一样,drf默认的渲染有2种方式,一种是json格式,另一种是模板方式. 渲染模块源码入口 入口:APIView类中dispatch方法中的:self.response ...

  4. 【 js 模块加载 】深入学习模块化加载(node.js 模块源码)

    一.模块规范 说到模块化加载,就不得先说一说模块规范.模块规范是用来约束每个模块,让其必须按照一定的格式编写.AMD,CMD,CommonJS 是目前最常用的三种模块化书写规范.  1.AMD(Asy ...

  5. Django-restframework 之权限源码分析

    Django-restframework 之权限源码分析 一 前言 上篇博客分析了 restframework 框架的认证组件的执行了流程并自定义了认证类.这篇博客分析 restframework 的 ...

  6. 【 js 模块加载 】【源码学习】深入学习模块化加载(node.js 模块源码)

    文章提纲: 第一部分:介绍模块规范及之间区别 第二部分:以 node.js 实现模块化规范 源码,深入学习. 一.模块规范 说到模块化加载,就不得先说一说模块规范.模块规范是用来约束每个模块,让其必须 ...

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

    [摘要] 集群管理模块cluster浅析 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 概述 cluster模块是node.js中用于实现和管理 ...

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

    目录 一. 概述 二. 线程与进程 三. cluster模块源码解析 3.1 起步 3.2 入口 3.3 主进程模块master.js 3.4 子进程模块child.js 四. 小结 示例代码托管在: ...

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

    前言 在SOA.微服务架构流行的年代,许多复杂业务上需要支持多资源占用场景,而在分布式系统中因为某个资源不足而导致其它资源占用回滚的系统设计一直是个难点.我所在的团队也遇到了这个问题,为解决这个问题上 ...

随机推荐

  1. python常用模块介绍

    关于if __name__ == "__main__": 若执行文件为bin,调用文件为cal: 若在执行文件bin中执行print(__name__) 输出:__main__ 当 ...

  2. [shell]如何测试shell脚本,保证正确

    如何用最快最有效的方式进行测试? 很多开发的习惯是,二话不说,写完/拿到,就跑一把,看看输入,输出,想要的操作是否完成,也就过了. 其实这是十分不严谨的,若是未经过QA,风险还是相当大的. 以下即sh ...

  3. Babel7知识梳理

    Babel7 知识梳理 对 Babel 的配置项的作用不那么了解,是否会影响日常开发呢?老实说,大多情况下没有特别大的影响(毕竟有搜索引擎). 不过呢,还是想更进一步了解下,于是最近认真阅读了 Bab ...

  4. form表单无刷新提交

    Ajax最大的特点就是可以不刷新页面而实现数据的通信及更改页面信息.那么用AJAX进行后台通信传递字符串还是可以的,遇到上传文件该怎么办呢?基于安全考虑,JS是不能直接进行文件操作的,只好用原始的fr ...

  5. WIN7+QT5.2.0 连接oracle11g问题及解决方法

    用下面的代码建立连接之后,出现了几个问题 //连接数据库 QSqlDatabase db = QSqlDatabase::addDatabase("QOCI"); /**连接Ora ...

  6. centos7 设置 tomcat 开机自启

    1.创建文件 # vi /etc/systemd/system/tomcat.service [Unit] Description=Tomcat8540 After=syslog.target net ...

  7. 《你不知道的JavaScript(上)》笔记——动态作用域

    动态作用域让作用域作为一个在运行时就被动态确定的形式, 而不是在写代码时进行静态确定的形式.动态作用域并不关心函数和作用域是如何声明以及在何处声明的, 只关心它们从何处调用. 换句话说, 作用域链是基 ...

  8. CIEDE2000色差公式相关

    色差公式发展的三个重要的阶段:1976年以前(CIELAB和CIELUV的采用).1976年到2001年(CIEDE2000色差公式的推荐).2001年以后. 国际照明委员会1998年成立了技术委员会 ...

  9. vue2.0+vue-video-player实现hls播放的案例

    1. 安装依赖. npm install vue-video-player --save 2. 在main.js引入vue-video-player. import VueVideoPlayer fr ...

  10. Object.keys() 遍历对象

    Object.keys()方法的运用与数组遍历 Object.keys()用于获得由对象属性名组成的数组,可与数组遍历相结合使用,用起来效果杠杠滴.数组遍历可以用for()或forEach()来实现, ...