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

目录
  • 概述
  • 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: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

这里暂时先简单进行异常输出,来模拟异常处理。最后不要忘记 在配置文件中注册中间件。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: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

自定义状态码枚举类

# 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]
  • 自定义的异常类用于区分系统异常和业务来进行单独处理。
  • 状态码枚举则是用来记录对应的异常信息。

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

响应信息统一结果的封装

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

# 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

完善统一异常处理逻辑

# 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

应用场景

注册校验

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

	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,
    )

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

源代码

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

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

尾语

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

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

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

到此这篇关于python中通过Django捕获所有异常的处理的文章就介绍到这了,更多相关python Django捕获异常内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python Django搭建文件下载服务器的实现

    环境 win10 Python:3.6.7 Django:2.2.7 运行效果 1.创建 Django 项目 # 创建Download项目 django-admin startproject Download # 创建down_app app python manage.py startapp down_app 2.修改配置文件:settings.py Download/Download/settings.py 1.添加注册APP:down_app 2.设置模板文件路径:templates 3.

  • Python django中如何使用restful框架

    在使用django进行前后台分离开发时通常会搭配django-rest-framework框架创建RESTful风格的接口API.框架介绍及版本要求可参考官方地址:https://www.django-rest-framework.org 本文以创建man包含name.sex字段的API为实例学习django-rest-framework框架的使用. 主要包含下面5个步骤: 1.创建Django项目 2.创建ORM模型 3.加载Django REST Framework 4.序列化模型 5.创建

  • python Django的显示个人信息详解

    目录 用命令创建Django项目 1.将磁盘切换为D盘 2.在D盘的路径下创建Django项目 3.最后显示新建index文件夹 启动项目 1.输入以下命令启动项目 2.在浏览器中输入http://127.0.0.1:8001/,就可以看到运行结果 3.数据迁移 4.添加app 5.添加index的路径 6.新建一个urls.py文件 7.编写一个视图返回函数 8.在新建的urls文件:里面写入一下代码 9.最后显示结果 10.新建一个templates文件存放welcome.html 11.编

  • python Django框架快速入门教程(后台管理)

    Python下有许多款不同的 Web 框架.Django是重量级选手中最有代表性的一位.许多成功的网站和APP都基于Django. Django 是一个开放源代码的 Web 应用框架,由 Python 写成. Django 遵守 BSD 版权,初次发布于 2005 年 7 月, 并于 2008 年 9 月发布了第一个正式版本 1.0 . Django 采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template). 参考官方文档:Django官方文档https:

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

    目录 概述 Django 统一异常处理 统一异常处理具体设计 自定义异常模块 自定义状态码枚举类 响应信息统一结果的封装 完善统一异常处理逻辑 应用场景 注册校验 源代码 尾语 概述 在项目中统一异常处理,可以防止代码中有未捕获的异常出现.本文介绍如何在 Django 项目中进行统一异常的处理,再结合状态码枚举类对项目异常信息进行日志记录. Django 统一异常处理 在 Django 项目中可以自定义 中间件类 继承 django.middleware.common 下的 Middleware

  • 详解python中的异常捕获

    异常 异常是程序发生错误的信号,程序一旦出错就会抛出异常,程序的运行随之终止. # 异常处理的三个特征 - 异常的追踪信息 - 异常的类型 - 异常的内容 捕获异常的目的:为了增强程序的健壮性,即便程序运行过程中出错,也不要终止程序,而是捕获异常并处理,将出错信息记录到日志内. # 语法上错误SyntaxError - 处理方式1:必须在程序运行前就改正 # 逻辑上的错误 - 错误发生的条件是可以预知的 --> if判断 - 错误发生的条件是无法预知的 --> 异常捕获 try 本来程序一旦出

  • Python中使用django form表单验证的方法

    一. django form表单验证引入 有时时候我们需要使用get,post,put等方式在前台HTML页面提交一些数据到后台处理例 ; <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Form</title> </head> <body> <div> <for

  • Python中的Django基本命令实例详解

    一.新建项目 $django-admin.py  startproject  project_name # 特别是在 windows 上,如果报错,尝试用 django-admin 代替 django-admin.py 试试 注意 project_name 是自己的项目名称,需要为合法的 Python 包名,如不能为 1a 或 a-b. 二.新建APP 要先进入项目目录下,cd project_name 然后执行下面的命令: $ python manage.py startapp app_nam

  • python中安装django模块的方法

    网上搜一下对应的版本号,版本号相对应. 安装django有两种方式: 1.pip安装 pip install django 这个方法我用的时候已经报错.貌似访问被阻挡.我一般都用第二种 2.下载压缩包手动安装 在django官网下载tar.gz包,放在python对应目录中解压缩 命令窗口进入压缩包目录后,执行python setup.py install 安装完成后,命令窗口导入django. 可以正常导入且看到版本号即安装成功. 之后就可以创建django项目了.pycharm有免费版和专业

  • python中的django是做什么的

    Django是什么? Django是一个基于Python的Web应用框架.它与Python的另外一个Web 框架 Flask最大的区别是,它奉行 "包含一切" 的哲学.该理念即为:创建 Web 应用所需的通用功能都应该包含到框架中,而不应存在于独立的软件包中.例如,身份验证. URL 路由. 模板系统. 对象关系映射 (ORM) 和 数据库迁移等功能都已包含在Django 框架中.虽然看上去失去了一些弹性,但是却可以让你在构建网站的时候更加有效率. 由于Django最初是被开发来用于管

  • 用实例详解Python中的Django框架中prefetch_related()函数对数据库查询的优化

    实例的背景说明 假定一个个人信息系统,需要记录系统中各个人的故乡.居住地.以及到过的城市.数据库设计如下: Models.py 内容如下: from django.db import models class Province(models.Model): name = models.CharField(max_length=10) def __unicode__(self): return self.name class City(models.Model): name = models.Ch

  • 在Python中的Django框架中进行字符串翻译

    使用函数 ugettext() 来指定一个翻译字符串. 作为惯例,使用短别名 _ 来引入这个函数以节省键入时间. 在下面这个例子中,文本 "Welcome to my site" 被标记为待翻译字符串: from django.utils.translation import ugettext as _ def my_view(request): output = _("Welcome to my site.") return HttpResponse(output

  • Python中django学习心得

    以下是作者在学习Python中django框架时的学习笔记,并把测试的代码做了详细分析,最后还附上了学习心得,值得大家学习. URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码. '''urlpatterns = [url(正则表达式, views视图函数,参数,别名),]参数说明:一个正则表达式字符串一个可调用对象,通常为一个视图函数或

  • Python中Django框架利用url来控制登录的方法

    本文实例讲述了Python中Django框架利用url来控制登录的方法.分享给大家供大家参考.具体如下: from django.conf.urls.defaults import patterns,url #or use login_required from django.contrib.admin.views.decorators import staff_member_required def login_url(regex, view, *p,**args): """

随机推荐