Django 统一异常处理

简介: Django 统一异常处理

概述

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

<br/>

Django 统一异常处理

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

<br/>

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

# middlewares.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 项目中间件模块 }
# @Date: 2021/09/24 8:18
from django.middleware.common import MiddlewareMixin



class ExceptionMiddleware(MiddlewareMixin):
    """统一异常处理中间件"""

    def process_exception(self, request, exception):
        """
        统一异常处理
        :param request: 请求对象
        :param exception: 异常对象
        :return:
        """
        # 异常处理
        print(exception)
        return None

<br/>

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

统一异常处理中间件配置

<br/>

中间件 process_exception 方法介绍

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

  • 返回值是 None,页面会报 500 状态码错误,视图函数不会执行。
  • 返回值是 HttpResponse 对象,则是对应的响应信息,页面不会报错。

<br/>

中间件中的方法

方法 作用
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) 视图函数之后执行

<br/>

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

django-middleware.png

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

<br/>

统一异常处理具体设计

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

<br/>

自定义异常模块

# exceptions.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 项目异常模块 }
# @Date: 2021/09/24 8:14

class 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):
    """业务异常类"""
    pass


class APIException(CommonException):
    """接口异常类"""
    pass

<br/>

自定义状态码枚举类

# enums.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 项目枚举类模块 }
# @Date: 2021/09/23 23:37

from enum import Enum


class 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]

<br/>

  • 自定义的异常类用于区分系统异常和业务来进行单独处理。
  • 状态码枚举则是用来记录对应的异常信息。
状态码枚举类的设计可以查阅 巧用Python 枚举类设计状态码信息

<br/>

响应信息统一结果的封装

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

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 项目信息返回结果模块 }
# @Date: 2021/09/23 22:10
from .enums import StatusCodeEnum


class 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

<br/>

完善统一异常处理逻辑

# middlewares.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 项目中间件模块 }
# @Date: 2021/09/24 8:18
import logging

from django.db import DatabaseError
from django.http.response import JsonResponse
from django.http import HttpResponseServerError
from django.middleware.common import MiddlewareMixin

from meiduo_mall.utils.result import R
from meiduo_mall.utils.enums import StatusCodeEnum
from meiduo_mall.utils.exceptions import BusinessException

logger = 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

<br/>

应用场景

注册校验

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

<br/>

    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

<br/>

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

    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)

<br/>

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

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

<br/>

# 创建用户
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,
    )

<br/>

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

<br/>

源代码

可能通过文章方式不好理解其思想,大家可以通过项目源代码的方式来参考。

美多商城 https://gitee.com/huiDBK/meiduo_project/tree/master

<br/>

尾语

✍ 用 Code 谱写世界,让生活更有趣。❤️

✍ 万水千山总是情,点赞再走行不行。❤️

✍ 码字不易,还望各位大侠多多支持。❤️

相关文章
|
2月前
|
Python
Django+DRF 实战:自定义异常处理流程
本文详解DRF异常处理流程,包括默认处理机制与自定义异常处理器的实现方法。通过源码分析和实战示例,讲解如何全局捕获并统一返回错误信息,并结合日志记录与友好提示提升项目健壮性。适用于Django进阶开发。
130 4
|
3月前
|
Linux 数据库 数据安全/隐私保护
Python web Django快速入门手册全栈版,共2590字,短小精悍
本教程涵盖Django从安装到数据库模型创建的全流程。第一章介绍Windows、Linux及macOS下虚拟环境搭建与Django安装验证;第二章讲解项目创建、迁移与运行;第三章演示应用APP创建及项目汉化;第四章说明超级用户创建与后台登录;第五章深入数据库模型设计,包括类与表的对应关系及模型创建步骤。内容精炼实用,适合快速入门Django全栈开发。
117 1
|
搜索推荐 前端开发 数据可视化
【优秀python web毕设案例】基于协同过滤算法的酒店推荐系统,django框架+bootstrap前端+echarts可视化,有后台有爬虫
本文介绍了一个基于Django框架、协同过滤算法、ECharts数据可视化以及Bootstrap前端技术的酒店推荐系统,该系统通过用户行为分析和推荐算法优化,提供个性化的酒店推荐和直观的数据展示,以提升用户体验。
533 1
【优秀python web毕设案例】基于协同过滤算法的酒店推荐系统,django框架+bootstrap前端+echarts可视化,有后台有爬虫
|
机器学习/深度学习 数据采集 数据可视化
基于爬虫和机器学习的招聘数据分析与可视化系统,python django框架,前端bootstrap,机器学习有八种带有可视化大屏和后台
本文介绍了一个基于Python Django框架和Bootstrap前端技术,集成了机器学习算法和数据可视化的招聘数据分析与可视化系统,该系统通过爬虫技术获取职位信息,并使用多种机器学习模型进行薪资预测、职位匹配和趋势分析,提供了一个直观的可视化大屏和后台管理系统,以优化招聘策略并提升决策质量。
733 4
|
10月前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
609 45
|
机器学习/深度学习 人工智能 算法
植物病害识别系统Python+卷积神经网络算法+图像识别+人工智能项目+深度学习项目+计算机课设项目+Django网页界面
植物病害识别系统。本系统使用Python作为主要编程语言,通过收集水稻常见的四种叶片病害图片('细菌性叶枯病', '稻瘟病', '褐斑病', '稻瘟条纹病毒病')作为后面模型训练用到的数据集。然后使用TensorFlow搭建卷积神经网络算法模型,并进行多轮迭代训练,最后得到一个识别精度较高的算法模型,然后将其保存为h5格式的本地模型文件。再使用Django搭建Web网页平台操作界面,实现用户上传一张测试图片识别其名称。
408 22
植物病害识别系统Python+卷积神经网络算法+图像识别+人工智能项目+深度学习项目+计算机课设项目+Django网页界面
|
机器学习/深度学习 算法 TensorFlow
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
交通标志识别系统。本系统使用Python作为主要编程语言,在交通标志图像识别功能实现中,基于TensorFlow搭建卷积神经网络算法模型,通过对收集到的58种常见的交通标志图像作为数据集,进行迭代训练最后得到一个识别精度较高的模型文件,然后保存为本地的h5格式文件。再使用Django开发Web网页端操作界面,实现用户上传一张交通标志图片,识别其名称。
460 7
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
|
10月前
|
安全 数据库 开发者
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第26天】本文详细介绍了如何在Django框架下进行全栈开发,包括环境安装与配置、创建项目和应用、定义模型类、运行数据库迁移、创建视图和URL映射、编写模板以及启动开发服务器等步骤,并通过示例代码展示了具体实现过程。
342 2
|
前端开发 搜索推荐 算法
中草药管理与推荐系统Python+Django网页界面+推荐算法+计算机课设系统+网站开发
中草药管理与推荐系统。本系统使用Python作为主要开发语言,前端使用HTML,CSS,BootStrap等技术和框架搭建前端界面,后端使用Django框架处理应用请求,使用Ajax等技术实现前后端的数据通信。实现了一个综合性的中草药管理与推荐平台。具体功能如下: - 系统分为普通用户和管理员两个角色 - 普通用户可以登录,注册、查看物品信息、收藏物品、发布评论、编辑个人信息、柱状图饼状图可视化物品信息、并依据用户注册时选择的标签进行推荐 和 根据用户对物品的评分 使用协同过滤推荐算法进行推荐 - 管理员可以在后台对用户和物品信息进行管理编辑
425 12
中草药管理与推荐系统Python+Django网页界面+推荐算法+计算机课设系统+网站开发
|
机器学习/深度学习 人工智能 算法
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
文本分类识别系统。本系统使用Python作为主要开发语言,首先收集了10种中文文本数据集("体育类", "财经类", "房产类", "家居类", "教育类", "科技类", "时尚类", "时政类", "游戏类", "娱乐类"),然后基于TensorFlow搭建CNN卷积神经网络算法模型。通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型,并保存为本地的h5格式。然后使用Django开发Web网页端操作界面,实现用户上传一段文本识别其所属的类别。
365 1
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台