全局异常处理
目录
一、全局异常处理
# APIView的dispatch方法中运行了三大认证,然后运行了视图类的方法,如果出了异常,会被异常捕获,捕获后统一处理
def dispatch(self, request, *args, **kwargs):
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
# drf 内置了一个函数,只要上面过程出了异常,就会执行这个函数,这个函数只处理的drf的异常
-主动抛的非drf异常
-程序出错了
都不会被处理
我们的目标,无论主动抛还是程序运行出错,都同意返回规定格式--》能记录日志
公司里一般返回 {code:999,'msg':'系统错误,请联系系统管理员'}
# 写一个函数,内部处理异常,在配置文件中配置一下即可
'第一步:首先在drf的配置文件中有一个配置信息如下(这个后面再配置,先挑出来讲而已)'
# Exception handling
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'这个配置就是处理drf内部异常的配置文件,如果我们想要进行自定义,就需要在dango的配置文件中自行注册进行替换'
'第二步:我们查看dispatch中的异常捕获,发现handle_exception处理了这些异常捕获的信息,而他的参数就是错误信息'
response = self.handle_exception(exc)
'第三步:我们进入他的源码发现他返回的信息是response,并且这个response是由exception_handler方法获得的'
def handle_exception(self, exc):
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()
response = exception_handler(exc, context)
if response is None:
self.raise_uncaught_exception(exc)
response.exception = True
return response
'第四步:我们可以看到他则是来自上面的一行代码'
exception_handler = self.get_exception_handler()
'第五步:进入这个get_exception_handler方法的源码,我们可以发现他就是拿了配置文件中的配置返回出去'
def get_exception_handler(self):
return self.settings.EXCEPTION_HANDLER
'而配置信息中对应的是一个函数,因此exception_handler的值就相当于是获取这个函数的结果'
def exception_handler(exc, context):
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
'研究了源码之后我们就开始自己编写,因为原本的异常处理函数有两个参数,因此我们也需要上两个参数,而它的源码太多了,我们可以跟面向对象的派生方法一样,在我们自行定义的函数内调用原本的函数,在此基础上进行自定义'
def common_exception_handler(exc, context):
# exc 错误对象
# context:上下文,有view:当前出错的视图类的对象,args和kwargs视图类方法分组出来的参数,request:当次请求的request对象
# 只要走到这里,就要记录日志 ,只有错了,才会执行这个函数
# 记录日志尽量详细
print('时间,登录用户id,用户ip,请求方式,请求地址,执行的视图类,错误原因')
res = exception_handler(exc, context)
if res: # 通过观察原来的函数我们发现有值的时候,说明返回了Response 对象,没有值说明返回None
# 如果是Response 对象说明是drf的异常,已经被处理了,如果是None表明没有处理,就是非drf的异常
res = Response(data={'code': 888, 'msg': res.data.get('detail', '请联系系统管理员')}) # 这里就是自定义返回的信息的格式,处理的就是原本的drf中的报错
else:
# res = Response(data={'code': 999, 'msg': str(exc)})
# 记录日志
res = Response(data={'code': 999, 'msg': '系统错误,请联系系统管理员'})
# 这里就是处理非drf报错的代码,如果想看具体报错可以把msg的值换成参数中的exc接收的错误信息
return res
# 在配置文件中配置
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler',
}