全局异常捕获
回顾:APIView的dispatch的时候--》三大认证,视图类的方法中--》出了异常--》被异常捕获--》都会执行一个函数:
-dispatch的大约 509 行 response = self.handle_exception(exc)
-只要出了异常,都会执行response = self.handle_exception(exc)
-handle_exception 源码分析
def handle_exception(self, exc):
# 拿到一个函数内存地址--》配置文件中配置了
# self.settings.EXCEPTION_HANDLER
# 拿到这个函数:rest_framework.views.exception_handler
exception_handler = self.get_exception_handler()
# 执行这个函数,传入俩参数
response = exception_handler(exc, context)
# drf的 Response的对象
return response
-rest_framework.views.exception_handler 逻辑分析
-判断异常是不是 drf内 exceptions.APIException的异常
-如果是--》组装了Response返回了
-如果不是--》返回了None
-总结:默认drf只处理了自己的异常,django的异常,它没处理,直接前端能看到
- APIException,是drf种所有异常的基类
如何使用
# 1. 新建一个模块
# 2. 进入到这个模块,导入drf自带的异常处理模块,以及处理逻辑
# 3. 全局配置异常
# 配置全局异常处理
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler',
}
获取异常字段
异常 | 错误类型 | 如何获取 |
---|---|---|
AuthenticationFailed | 列表 | response[0] |
ValidationError | 字典 | response.get("detail") |
自定义异常
class PasswordException(Exception):
def __init__(self, msg):
self.msg = msg
import time
from user_agents import parse
from datetime import datetime
from rest_framework.response import Response
from rest_framework.views import exception_handler
# 自定义异常
class PasswordException(Exception):
def __init__(self, msg):
self.msg = msg
# 全局异常捕获
def common_exception_handler(exc, context):
# exc 就是异常 Exception as exc 简写了
"""
context 是一个字典 可以通过get获取对应的键,进一步去处理
'view': 错误发生在哪一个视图层
'args': 不定长位置参数
'kwargs': 不定长关键字参数
'request': 这个request,就是正常的request,可以通过这个取到客户的IP地址,哪一个用户等,方便记录日志
"""
# 拿到 request
request = context.get("request")
# 拿到视图
view = context.get("view")
# 拿到IP地址
ip = request.META.get("REMOTE_ADDR")
# 拿到请求方式
method = request.method
# 拿到请求路径
path = request.get_full_path()
# 拿到user_agent
user_agent = request.META.get("HTTP_USER_AGENT")
# 拿到请求浏览器
ua = parse(user_agent)
browser = ua.browser.family
# 获取当前时间
now = datetime.fromtimestamp(int(time.time()))
# 是否登录用户
user_type = request.user or "匿名用户"
data = f"[用户]:{user_type} [IP]:{ip} [路径]:{path} [方式]:{method} / {browser} [视图]:{view.__class__} [时间]:{now}"
print(data)
# 返回response说明是drf的异常
# 返回的是None说明并不是drf的异常
# 这个response 就是drf的Response对象
response = exception_handler(exc, context)
if response:
if isinstance(response.data, list):
detail = response.data[0]
elif isinstance(response.data, dict):
detail = response.data.get("detail", "系统错误,请联系管理员。")
else:
detail = "系统错误,请联系管理员"
return Response({"code": 900, "msg": detail})
else:
err = str(exc)
return Response({"code": 101, "error": err})
# 视图
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import ValidationError, AuthenticationFailed, APIException
from .exceptions import PasswordException
class UserView(APIView):
def get(self, request):
# 9 / 0
raise PasswordException("密码错误!")
一些建议
# 只要程序走到了我们自定义的异常模块,就说明程序肯定出错了。
# 统一的返回格式。
- 因为项目上线后,要关闭调试模式,肯定要有一个统一的返回格式。
# 网络错误,请稍后再试。
# 系统错误,请联系管理员。
# 使用日志去记录,越详细越好,这样可以更快的去排查错误。
- 日志放在函数内部最上面就可以了,因为只要进入到这里程序肯定出错了。
- 一般会记录:请求方式,请求地址,客户端IP,用户角色。。。
标签:exception,07,get,捕获,request,self,全局,异常,response
From: https://www.cnblogs.com/ccsvip/p/18143834