小编给大家分享一下如何使用Django与DRF实现全局异常处理方案,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

实现的目标

如果没有 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中)。

我们进一步查看这个函数的源码:

defexception_handler(exc,context):"""Returnstheresponsethatshouldbeusedforanygivenexception.BydefaultwehandletheRESTframework`APIException`,andalsoDjango'sbuilt-in`Http404`and`PermissionDenied`exceptions.Anyunhandledexceptionsmayreturn`None`,whichwillcausea500errortoberaised."""ifisinstance(exc,Http404):exc=exceptions.NotFound()elifisinstance(exc,PermissionDenied):exc=exceptions.PermissionDenied()ifisinstance(exc,exceptions.APIException):headers={}ifgetattr(exc,'auth_header',None):headers['WWW-Authenticate']=exc.auth_headerifgetattr(exc,'wait',None):headers['Retry-After']='%d'%exc.waitifisinstance(exc.detail,(list,dict)):data=exc.detailelse:data={'detail':exc.detail}set_rollback()returnResponse(data,status=exc.status_code,headers=headers)returnNone

通过这个函数的文档签名我们知道,DRF 会处理所有继承自APIException的异常类,并且还会额外的处理 Django 内置的Http404PermissionDenied异常,并将这些异常的处理结果返回到前台。

如果不再这些处理范围之内,函数会返回None,这时候会给 Django 抛出一个 500 的服务器错误异常。

DRF 支持单独配置异常处理函数,因此第一步现在 setting 中指定自定义的异常处理函数的位置:

REST_FRAMEWORK={'EXCEPTION_HANDLER':'server.exception.exception_globe.globe_exception_handler'}

然后我们定义自己的异常处理程序:

第一步,调用 DRF 自己的异常处理函数第二步,对 DRF 拦截的异常进行处理第三步,将其他异常抛给 Django 处理

defglobe_exception_handler(exc,context):"""BelowistheglobalexceptionhandlerofdrfHttp404/PermissionDenied/APIException"""#CallRESTframework'sdefaultexceptionhandlerresponse=exception_handler(exc,context)request=context['request']#ExceptionsformDRFandDjangobuilt-in`Http404`and`PermissionDenied`ifresponseisnotNone:ifisinstance(response.data,list):msg=';'.join(response.data)elifisinstance(response.data,str):msg=response.dataelse:msg='Sorry,wemakeamistake(* ̄︶ ̄)!'ex_data={"msg":msg,"error_code":1000,"request":request.path}returnJsonResponse(data=ex_data,status=response.status_code)#Exceptionsfromothers#如果response为None则直接触发上面的ExceptionGlobeMiddlewarereturnresponseDjango 异常处理方案

从上一步的结果我们知道,DRF 处理不了的异常我们抛给了 Django,而 Django 支持通过定义中间件进行全局异常处理,因此接下来我们只需要定一个 Django 全局异常处理的中间件,并将中间件配置到 setting 文件中的 MIDDLEWARE 数组即可。

try:fromdjango.utils.deprecationimportMiddlewareMixin#Django1.10.xexceptImportError:MiddlewareMixin=object#Django1.4.x-Django1.9.xclassExceptionGlobeMiddleware(MiddlewareMixin):"""Belowistheglobalexceptionhandlerofdjango"""defprocess_exception(self,request,exception):#直接抛出djangoadmin的异常ifstr(request.path).startswith('/admin/'):returnNone#捕获其他异常,直接返回500ex_data={"msg":"Sorry,wemakeamistake(* ̄︶ ̄)!","error_code":1000,"request":request.path}returnJsonResponse(data=ex_data,status=500)

值得注意的是,我们可以在中间件的处理函数中拿到request对象,因此,我们可以通过这个对象拿到用户请求的 url,这样,我们通过判断 url 就可以得到那些请求是来自 Django 自带的 admin 的。

参考代码如下:

#直接抛出djangoadmin的异常ifstr(request.path).startswith('/admin/'):returnNone

以上是“如何使用Django与DRF实现全局异常处理方案”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!