Django与DRF结合的全局异常处理方案详解
作者:Cao 发布时间:2021-05-19 22:53:16
前言
Django 和 DRF(django rest framawork
) 的结合在 python 后台中经常出现的组合。对于异常的全局处理,我们系统能有一个统一的解决方案,在开发环境能看到比较全的异常堆栈,而在生产环境能更好的给用户一个友好的提示,本文旨在提出一个统一个全局异常处理方案,仅供参考使用。
实现的目标
如果没有 DRF,我们只需要在 Django 中加一个中间件就可以解决全局异常的处理问题,但是 DRF 会帮我们处理一些异常并自动返回到客户端,因此我们要协调两者的异常处理策略。
同时我们希望能使用 Django 的 admin 进行一些后台的数据查看和修改,因此最好要保留 admin 的内部异常处理行为。
本文的目标如下:
保留 Django 自带的 admin 的异常处理行为
拦截 DRF 的异常并进行全局异常行为处理
拦截除 DRF 的异常之外的其他 Django 异常并进行全局异常行为处理
DRF 全局异常拦截的解决思路
首先 DRF 的异常都是继承自 APIException
这个类的,并且 DRF 跑出的异常会被 exception_handler
这个异常处理函数拦截(这个函数的位置在 /python3.7/site-packages/rest_framework/views.py
中)。
我们进一步查看这个函数的源码:
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 会处理所有继承自 APIException
的异常类,并且还会额外的处理 Django 内置的 Http404
和 PermissionDenied
异常,并将这些异常的处理结果返回到前台。
如果不再这些处理范围之内,函数会返回 None
,这时候会给 Django 抛出一个 500 的服务器错误异常。
DRF 支持单独配置异常处理函数,因此第一步现在 setting 中指定自定义的异常处理函数的位置:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'server.exception.exception_globe.globe_exception_handler'
}
然后我们定义自己的异常处理程序:
第一步,调用 DRF 自己的异常处理函数第二步,对 DRF 拦截的异常进行处理第三步,将其他异常抛给 Django 处理
def globe_exception_handler(exc, context):
"""
Below is the global exception handler of drf
Http404 / PermissionDenied / APIException
"""
# Call REST framework's default exception handler
response = exception_handler(exc, context)
request = context['request']
# Exceptions form DRF and Django built-in `Http404` and `PermissionDenied`
if response is not None:
if isinstance(response.data, list):
msg = '; '.join(response.data)
elif isinstance(response.data, str):
msg = response.data
else:
msg = 'Sorry, we make a mistake (* ̄︶ ̄)!'
ex_data = {
"msg": msg,
"error_code": 1000,
"request": request.path
}
return JsonResponse(data=ex_data, status=response.status_code)
# Exceptions from others
# 如果 response 为 None 则直接触发上面的 ExceptionGlobeMiddleware
return response
Django 异常处理方案
从上一步的结果我们知道,DRF 处理不了的异常我们抛给了 Django,而 Django 支持通过定义中间件进行全局异常处理,因此接下来我们只需要定一个 Django 全局异常处理的中间件,并将中间件配置到 setting 文件中的 MIDDLEWARE 数组即可。
try:
from django.utils.deprecation import MiddlewareMixin # Django 1.10.x
except ImportError:
MiddlewareMixin = object # Django 1.4.x - Django 1.9.x
class ExceptionGlobeMiddleware(MiddlewareMixin):
"""
Below is the global exception handler of django
"""
def process_exception(self, request, exception):
# 直接抛出 django admin 的异常
if str(request.path).startswith('/admin/'):
return None
# 捕获其他异常,直接返回 500
ex_data = {
"msg": "Sorry, we make a mistake (* ̄︶ ̄)!",
"error_code": 1000,
"request": request.path
}
return JsonResponse(data=ex_data, status=500)
值得注意的是,我们可以在中间件的处理函数中拿到 request
对象,因此,我们可以通过这个对象拿到用户请求的 url,这样,我们通过判断 url 就可以得到那些请求是来自 Django 自带的 admin 的。
参考代码如下:
# 直接抛出 django admin 的异常
if str(request.path).startswith('/admin/'):
return None
来源:https://blog.csdn.net/crown_0726/article/details/123052188


猜你喜欢
- 回顾一下已经了解的数据类型:int/str/bool/list/dict/tuple还真的不少了.不过,python是一个发展的语言,没准以
- 方法一 通过GridView(简评:方法比较简单,但是只适合生成格式简单的Excel,且无法保留VBA代码),页面无刷新aspx.cs部分u
- 图形化验证码生成和验证功能介绍在使用用户名和密码登录功能时,需要填写验证码,验证码是以图形化的方式进行获取和展示的。验证码使用原理验证码的使
- pandas 保存数据到excel,csvpandas 保存数据比较简单对于任意一个dataframe:import pandas as p
- 软件环境: 1、操作系统:Windows 2000 Server 2、数 据 库:Oracle 8i R2 (8.1.7) for NT 企
- SQL Server 在处理存储过程的时候,为了节省编译时间,是一次编译,多次重用。当第一次运行时代入值产生的执行计划,不适用后续代入的参数
- torch.nn.BatchNorm1d()1、BatchNorm1d(num_features, eps = 1e-05, momentu
- 有以下两种方式启用警告框的可取消(dismissal)功能:1、通过 data 属性:通过数据 API(Data API)添加可取消功能,只
- 本文实例为大家分享了js实现弹幕特效的具体代码,供大家参考,具体内容如下此处使用HBuilder编译,最简单的弹幕效果,希望各位前辈不吝指教
- 一年中秋至 又见圆月时导语假设农历八月十五,程序员错过了今年的中秋圆月。▼程序员的苦只有他们寄几知道bug,bug,bug,bug,bug,
- replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。语法:string.replace(sub
- mysql是一个优秀的开源数据库,它现在的应用非常的广泛,因此很有必要简单的介绍一下用python操作mysql数据库的方法。python操
- 本文实例讲述了Python实现将SQLite中的数据直接输出为CVS的方法。分享给大家供大家参考,具体如下:对于SQLite来说,目前查看还
- 6、遮罩滤镜作用:该滤镜可以为对象建立一个覆盖于表面的膜,其效果就象戴着有色眼镜看物体一样。语法: {filter:mask(color=c
- openpyxl打开的文件是否需要关闭如果是只读或者只写的模式下,是需要再调用close()方法关闭workbook,正常的情况下是不需要调
- 本文实例讲述了Python实现测试磁盘性能的方法。分享给大家供大家参考。具体如下:该代码做了如下工作:create 300000 files
- 最近在研究文本挖掘相关的内容,所谓巧妇难为无米之炊,要想进行文本分析,首先得到有文本吧。获取文本的方式有很多,比如从网上下载现成的文本文档,
- 本文实例讲述了Python实现模拟登录及表单提交的方法。分享给大家供大家参考。具体实现方法如下:# -*- coding: utf-8 -*
- 将 numpy 数组存入文件,有多种文件类型可供选择,对应地就有不同的方法来读写。下面我将介绍读写 numpy 的三类文件:txt 或者 c
- 什么是拼音转换在我们学习语言之前,我们一般会学习拼音来认识汉字,并学会如何读汉字。所以,拼音在对于我们语言的重要性不言而喻。而拼音转换指的是