全局异常处理
一. 常见类型(DRF 的异常)
异常类型 | 异常解释 |
---|---|
APIException | 所有异常的父类 |
ParseError | 解析错误 |
AuthenticationFailed | 认证失败 |
NotAuthenticated | 尚未认证 |
PermissionDenied | 权限决绝 |
NotFound | 未找到 |
MethodNotAllowed | 请求方式不支持 |
NotAcceptable | 要获取的数据格式不支持 |
Throttled | 超过限流次数 |
二. 异常处理
1. 代码示例
创建一个 py 文件 例如:exceptions.py
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
# 重写 exception_handler 函数
def common_exception_handler(exc, context):
"""
# 视图类的对象
view = context['view']
# 当前请求的对象, ip,用户id,当前时间,请求地址来
request = context['request']
print('ip地址为:%s的用户,访问:%s 视图类,报错了,请求地址是:%s'%(request.META.get('REMOTE_ADDR'),str(view),request.path))
"""
# 在这里可以记录日志
# 日志记录,越详细越好:哪个用户(id,ip),在什么时间,执行哪个视图函数时报了错,请求地址是什么
logger.error(f'用户:{user_id}, 请求方法:{request.method}, 请求路径:{request.path}, 视图函数: {view}, 错误:{exc}')
# """ ------------------以下为主要代码---------------------- """"
# res 有值则是 drf 异常, 值为 None 则是 django 异常
res = drf_exception_handler(exc, context)
# 在这里,可以通过状态码,把异常分的更细一些: 比如-----> 有数据的异常,除以0的异常,列表越界异常。.
if res:
# 这是drf的异常,其实人家已经处理了,但是不符合我的格式 。
res = Response({'code':999,'msg':response.data.get('detail', "服务器出错,请联系系统管理员")})
else:
res = Response({'code':998,'msg':str(exc)})
# 一定不要忘记 return
return res
2. 注册使用
REST_FRAMEWORK = {
# 自己写的全局异常捕获 《 值为文件路径 》
'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler',
}
3. 源码刨析
(1). APIView 执行异常处理的过程
# 1. 导入 APIView
from rest_framework.views import APIView
# 2. 发现 APIView 源码中有一个异常处理函数 handle_exception 代码如下:《只看注释部分》
def handle_exception(self, exc):
if isinstance(exc, (exceptions.NotAuthenticated, exceptions.AuthenticationFailed)):
auth_header = self.get_authenticate_header(self.request)
if auth_header:
exc.auth_header = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN
# 在这里获取异常处理函数,见下面代码 3
exception_handler = self.get_exception_handler()
context = self.get_exception_handler_context()
# 在这里执行异常处理函数
response = exception_handler(exc, context)
if response is None:
# 如果没有配置异常处理就直接抛出 exc 见代码 4
self.raise_uncaught_exception(exc)
response.exception = True
return response
# 3. get_exception_handler函数,还回了配置文件中的配置项 《就是自己写的异常处理函数,或 DRF 的》
def get_exception_handler(self):
return self.settings.EXCEPTION_HANDLER
# 4.
def raise_uncaught_exception(self, exc):
if settings.DEBUG:
request = self.request
renderer_format = getattr(request.accepted_renderer, 'format')
use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin')
request.force_plaintext_errors(use_plaintext_traceback)
raise exc
(2). DRF 的异常处理
# 导入
from rest_framework.views import exception_handler
"""以下是 DRF 源码部分对异常的处理"""
# DRF 的异常处理
def exception_handler(exc, context):
if isinstance(exc, Http404):
exc = exceptions.NotFound(*(exc.args))
elif isinstance(exc, PermissionDenied):
exc = exceptions.PermissionDenied(*(exc.args))
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
# -----------------------一下是源码的解释-------------------------
# 返回应用于任何给定异常的响应。
Returns the response that should be used for any given exception.
# 默认情况下,我们处理REST框架的APIException,和
# Django内置的' Http404 '和' PermissionDenied '异常
By default we handle the REST framework ‘APIException’, and also
Django’s built-in 'Http404' and 'PermissionDenied' exceptions。
# 任何未处理的异常都可能返回 'None',这将导致引发500错误
Any unhandled exceptions may return `None`, which will cause a 500 error to be raised.
三. 自定义异常
class MyCustomException(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return f"MyCustomException: {self.message}"
# 使用自定义异常
try:
raise MyCustomException("This is an error message")
except MyCustomException as e:
print(e) # 结果:MyCustomException: This is an error message