def dispatch(self, request, *args, **kwargs):
...
try:
...
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
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()
response = exception_handler(exc, context)
def get_exception_handler(self):
"""
Returns the exception handler that this view uses.
"""
return self.settings.EXCEPTION_HANDLER
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
也就是说,我们只要重写 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
if isinstance(exc, exceptions.APIException):
isinstance 方法判断 ecx 是否为 APIException 的实例对象,所有继承了 APIException 的异常都会被处理。也就是说,DRF 只处理自己的异常。REST framework定义的异常如下表格所示
异常 | 解释 |
---|---|
APIException | 所有异常的父类 |
ParseError | 解析错误 |
AuthenticationFailed | 认证失败 |
NotAuthenticated | 尚未认证 |
PermissionDenied | 权限决绝 |
NotFound | 未找到 |
MethodNotAllowed | 请求方式不支持 |
NotAcceptable | 要获取的数据格式不支持 |
Throttled | 超过限流次数 |
ValidationError | 校验失败 |
drf 默认只处理自己的异常:所有 drf 中抛的异常,都有 detail
而 django 的异常,抛出很长的 xml 数据
方法
from rest_framework.views import exception_handler
from rest_framework.response import Response
def common_exception_handler(exc, context):
response = exception_handler(exc, context)
if response:
res = Response(data={'code': 10001, 'msg': response.data.get('detail')})
else:
# res = Response({'code': 10002, 'msg': '服务器内部错误'})
res = Response({'code': 10002, 'msg': str(exc)})
return res
配置文件
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.tool.exception.common_exception_handler'
}
在自定义异常处理中,还需要记录日志,并且日志越详细越好,例如某个用户在什么时间执行了什么视图函数出现了什么异常以及请求的地址
在自定义异常处理方法中还有一个参数 context
,出现异常的时候打印如下所示
{
'view': <app01.view.ErrorView.Error object at 0x000001EC7F55D940>,
'args': (),
'kwargs': {},
'request': <rest_framework.request.Request: GET '/test/error/'>
}
我们可以利用这些属性来生成日志,例如:
'ip地址为:%s的用户,访问:%s 视图类,报错了,请求地址是:%s'%(request.META.get('REMOTE_ADDR'),str(view),request.path)