07-全局异常捕获

全局异常捕获

回顾: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,用户角色。。。

本文作者:小满三岁啦

本文链接:https://www.cnblogs.com/ccsvip/p/18143834

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   小满三岁啦  阅读(23)  评论(0编辑  收藏  举报
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑
点击右上角即可分享
微信分享提示
  1. 1 夜空中最亮的星 小果酱
夜空中最亮的星 - 小果酱
00:00 / 00:00
An audio error has occurred.