千家信息网

python中怎么通过Django捕获所有异常的处理

发表于:2025-02-13 作者:千家信息网编辑
千家信息网最后更新 2025年02月13日,这篇文章主要为大家展示了"python中怎么通过Django捕获所有异常的处理",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"python中怎么通过Djan
千家信息网最后更新 2025年02月13日python中怎么通过Django捕获所有异常的处理

这篇文章主要为大家展示了"python中怎么通过Django捕获所有异常的处理",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"python中怎么通过Django捕获所有异常的处理"这篇文章吧。

    概述

    在项目中统一异常处理,可以防止代码中有未捕获的异常出现。本文介绍如何在 Django 项目中进行统一异常的处理,再结合状态码枚举类对项目异常信息进行日志记录。

    Django 统一异常处理

    Django 项目中可以自定义 中间件类 继承 django.middleware.common 下的 MiddlewareMixin 中间件类,重写 process_exception 方法的异常处理逻辑,然后在项目配置下的 中间件中注册 即可进行全局异常处理。

    我是在项目自定义的 utils 包下 middlewares.py 模块中下进行中间件的编写。

    # middlewares.py#!/usr/bin/python3# -*- coding: utf-8 -*-# @Author: Hui# @Desc: { 项目中间件模块 }# @Date: 2021/09/24 8:18from django.middleware.common import MiddlewareMixinclass ExceptionMiddleware(MiddlewareMixin):    """统一异常处理中间件"""    def process_exception(self, request, exception):        """        统一异常处理        :param request: 请求对象        :param exception: 异常对象        :return:        """        # 异常处理        print(exception)        return None

    这里暂时先简单进行异常输出,来模拟异常处理。最后不要忘记 在配置文件中注册中间件。django 项目默认的配置文件是 settings.py 我这里只是把配置文件单独放到了 settings 包下然后改了文件名。

    process_exception 方法介绍

    process_exception 方法只有在视图函数中出现异常了才执行。该方法的返回值可以是一个 None 也可以是一个 HttpResponse 对象。

    • 返回值是 None,页面会报 500 状态码错误,视图函数不会执行。

    • 返回值是 HttpResponse 对象,则是对应的响应信息,页面不会报错。

    中间件中的方法

    方法作用
    process_request(self,request)在视图函数之前执行
    process_view(self, request, view_func, view_args, view_kwargs)视图函数之前,process_request 方法之后执行
    process_exception(self, request, exception)视图函数中出现异常了才执行
    process_response(self, request, response)视图函数之后执行

    下面一图就能比较好的呈现 django 整个处理流程逻辑

    更多的中间件细节可以去 Django 官方文档 进行了解。

    统一异常处理具体设计

    结合自定义的异常和状态码枚举类,进行异常日志信息和业务逻辑的处理。

    自定义异常模块

    # exceptions.py#!/usr/bin/python3# -*- coding: utf-8 -*-# @Author: Hui# @Desc: { 项目异常模块 }# @Date: 2021/09/24 8:14class CommonException(Exception):    """公共异常类"""    def __init__(self, enum_cls):        self.code = enum_cls.code        self.errmsg = enum_cls.errmsg        self.enum_cls = enum_cls      # 状态码枚举类        super().__init__()class BusinessException(CommonException):    """业务异常类"""    passclass APIException(CommonException):    """接口异常类"""    pass

    自定义状态码枚举类

    # enums.py#!/usr/bin/python3# -*- coding: utf-8 -*-# @Author: Hui# @Desc: { 项目枚举类模块 }# @Date: 2021/09/23 23:37from enum import Enumclass StatusCodeEnum(Enum):    """状态码枚举类"""    OK = (0, '成功')    ERROR = (-1, '错误')    SERVER_ERR = (500, '服务器异常')    IMAGE_CODE_ERR = (4001, '图形验证码错误')    THROTTLING_ERR = (4002, '访问过于频繁')    NECESSARY_PARAM_ERR = (4003, '缺少必传参数')    USER_ERR = (4004, '用户名错误')    PWD_ERR = (4005, '密码错误')    CPWD_ERR = (4006, '密码不一致')    MOBILE_ERR = (4007, '手机号错误')    SMS_CODE_ERR = (4008, '短信验证码有误')    ALLOW_ERR = (4009, '未勾选协议')    SESSION_ERR = (4010, '用户未登录')    REGISTER_FAILED_ERR = (4011, '注册失败')    DB_ERR = (5000, '数据库错误')    EMAIL_ERR = (5001, '邮箱错误')    TEL_ERR = (5002, '固定电话错误')    NODATA_ERR = (5003, '无数据')    NEW_PWD_ERR = (5004, '新密码错误')    OPENID_ERR = (5005, '无效的openid')    PARAM_ERR = (5006, '参数错误')    STOCK_ERR = (5007, '库存不足')    @property    def code(self):        """获取状态码"""        return self.value[0]    @property    def errmsg(self):        """获取状态码信息"""        return self.value[1]
    • 自定义的异常类用于区分系统异常和业务来进行单独处理。

    • 状态码枚举则是用来记录对应的异常信息。

    状态码枚举类的设计可以查阅 巧用Python 枚举类设计状态码信息

    响应信息统一结果的封装

    统一前后端交互数据和异常信息结果。

    # result.py#!/usr/bin/python3# -*- coding: utf-8 -*-# @Author: Hui# @Desc: { 项目信息返回结果模块 }# @Date: 2021/09/23 22:10from .enums import StatusCodeEnumclass R(object):    """    统一项目信息返回结果类    """    def __init__(self):        self.code = None        self.errmsg = None        self._data = dict()    @staticmethod    def ok():        """        组织成功响应信息        :return:        """        r = R()        r.code = StatusCodeEnum.OK.code        r.errmsg = StatusCodeEnum.OK.errmsg        return r    @staticmethod    def error():        """        组织错误响应信息        :return:        """        r = R()        r.code = StatusCodeEnum.ERROR.code        r.errmsg = StatusCodeEnum.ERROR.errmsg        return r    @staticmethod    def server_error():        """        组织服务器错误信息        :return:        """        r = R()        r.code = StatusCodeEnum.SERVER_ERR.code        r.errmsg = StatusCodeEnum.SERVER_ERR.errmsg        return r    @staticmethod    def set_result(enum):        """        组织对应枚举类的响应信息        :param enum: 状态枚举类        :return:        """        r = R()        r.code = enum.code        r.errmsg = enum.errmsg        return r    def data(self, key=None, obj=None):        """统一后端返回的数据"""        if key:            self._data[key] = obj        context = {            'code': self.code,            'errmsg': self.errmsg,            'data': self._data        }        return context

    完善统一异常处理逻辑

    # middlewares.py#!/usr/bin/python3# -*- coding: utf-8 -*-# @Author: Hui# @Desc: { 项目中间件模块 }# @Date: 2021/09/24 8:18import loggingfrom django.db import DatabaseErrorfrom django.http.response import JsonResponsefrom django.http import HttpResponseServerErrorfrom django.middleware.common import MiddlewareMixinfrom meiduo_mall.utils.result import Rfrom meiduo_mall.utils.enums import StatusCodeEnumfrom meiduo_mall.utils.exceptions import BusinessExceptionlogger = logging.getLogger('django')class ExceptionMiddleware(MiddlewareMixin):    """统一异常处理中间件"""    def process_exception(self, request, exception):        """        统一异常处理        :param request: 请求对象        :param exception: 异常对象        :return:        """        if isinstance(exception, BusinessException):            # 业务异常处理            data = R.set_result(exception.enum_cls).data()            return JsonResponse(data)        elif isinstance(exception, DatabaseError):            # 数据库异常            r = R.set_result(StatusCodeEnum.DB_ERR)            logger.error(r.data(), exc_info=True)            return HttpResponseServerError(StatusCodeEnum.SERVER_ERR.errmsg)        elif isinstance(exception, Exception):            # 服务器异常处理            r = R.server_error()            logger.error(r.data(), exc_info=True)            return HttpResponseServerError(r.errmsg)                return None

    应用场景

    注册校验

    让我们来看一段注册校验功能业务逻辑

     def verify_params(self, request):        """        校验注册信息        :param request: 注册请求对象        :return: response_ret        """        # 接受参数        self.username = request.POST.get('username')        self.password = request.POST.get('password')        self.confirm_pwd = request.POST.get('confirm_pwd')        self.mobile = request.POST.get('mobile')        self.allow = request.POST.get('allow')           if not all(all_args):            # raise BusinessException(StatusCodeEnum.PARAM_ERR)            response_ret = http.HttpResponseForbidden('参数错误')            return response_ret        # 用户名 5-20个字符        if not re.match(r'^[a-zA-Z0-9_]{5,20}', self.username):            response_ret = http.HttpResponseForbidden('用户名不规范')            return response_ret        # 密码 8-20个字符        if not re.match(r'^[a-zA-Z0-9]{8,20}', self.password):            response_ret = http.HttpResponseForbidden('密码不规范')            return response_ret        # 两次密码一致性        if self.password != self.confirm_pwd:            response_ret = http.HttpResponseForbidden('两次密码不一致')            return response_ret        # 手机号合法性        if not re.match(r'^1[3-9]\d{9}$', self.mobile):            response_ret = http.HttpResponseForbidden('手机号码不合法')            return response_ret        # 是否勾选用户协议        if self.allow != 'on':            response_ret = http.HttpResponseForbidden('请勾选用户协议')            return response_ret        return response_ret

    通过抛异常和设置状态码枚举来处理

    def verify_params(self, request):        """        校验注册信息        :param request: 注册请求对象        :return: response_ret        """        # 接受参数        self.username = request.POST.get('username')        self.password = request.POST.get('password')        self.confirm_pwd = request.POST.get('confirm_pwd')        self.mobile = request.POST.get('mobile')        self.allow = request.POST.get('allow')        # 校验参数        all_args = [self.username, self.password, self.confirm_pwd, self.mobile, self.allow]        if not all(all_args):            raise BusinessException(StatusCodeEnum.PARAM_ERR)        # 用户名 5-20个字符        if not re.match(r'^[a-zA-Z0-9_]{5,20}', self.username):            raise BusinessException(StatusCodeEnum.USER_ERR)        # 密码 8-20个字符        if not re.match(r'^[a-zA-Z0-9]{8,20}', self.password):            raise BusinessException(StatusCodeEnum.PWD_ERR)        # 两次密码一致性        if self.password != self.confirm_pwd:            raise BusinessException(StatusCodeEnum.CPWD_ERR)        # 手机号合法性        if not re.match(r'^1[3-9]\d{9}$', self.mobile):            raise BusinessException(StatusCodeEnum.MOBILE_ERR)        # 是否勾选用户协议        if self.allow != 'on':            raise BusinessException(StatusCodeEnum.ALLOW_ERR)

    减少 try ... except ... 代码块

    例如在对数据库进行操作时,为了防止数据库发生了意外的异常导致系统崩溃,通常加上 try ... except ...来记录异常信息。然而配置了全局异常处理,则可以不用管理。

    # 创建用户try:    user = User.objects.create_user(        username=self.username,        password=self.password,        mobile=self.mobile,    )except DatabaseError as e:    logger.error(e)        # 有了全局的异常处理user = User.objects.create_user(        username=self.username,        password=self.password,        mobile=self.mobile,    )

    注意:如果需要通过异常捕获来处理一些业务信息,则不可避免,如事务回滚等

    以上是"python中怎么通过Django捕获所有异常的处理"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

    0