python中通過Django捕獲所有異常的處理
概述
在項(xiàng)目中統(tǒng)一異常處理,可以防止代碼中有未捕獲的異常出現(xiàn)。本文介紹如何在 Django
項(xiàng)目中進(jìn)行統(tǒng)一異常的處理,再結(jié)合狀態(tài)碼枚舉類對(duì)項(xiàng)目異常信息進(jìn)行日志記錄。
Django 統(tǒng)一異常處理
在 Django
項(xiàng)目中可以自定義 中間件類 繼承 django.middleware.common
下的 MiddlewareMixin
中間件類,重寫 process_exception
方法的異常處理邏輯,然后在項(xiàng)目配置下的 中間件中注冊(cè) 即可進(jìn)行全局異常處理。
我是在項(xiàng)目自定義的 utils
包下 middlewares.py
模塊中下進(jìn)行中間件的編寫。
# middlewares.py #!/usr/bin/python3 # -*- coding: utf-8 -*- # @Author: Hui # @Desc: { 項(xiàng)目中間件模塊 } # @Date: 2021/09/24 8:18 from django.middleware.common import MiddlewareMixin class ExceptionMiddleware(MiddlewareMixin): """統(tǒng)一異常處理中間件""" def process_exception(self, request, exception): """ 統(tǒng)一異常處理 :param request: 請(qǐng)求對(duì)象 :param exception: 異常對(duì)象 :return: """ # 異常處理 print(exception) return None
這里暫時(shí)先簡(jiǎn)單進(jìn)行異常輸出,來模擬異常處理。最后不要忘記 在配置文件中注冊(cè)中間件。django
項(xiàng)目默認(rèn)的配置文件是 settings.py
我這里只是把配置文件單獨(dú)放到了 settings
包下然后改了文件名。
process_exception
方法介紹
process_exception
方法只有在視圖函數(shù)中出現(xiàn)異常了才執(zhí)行。該方法的返回值可以是一個(gè) None
也可以是一個(gè) HttpResponse
對(duì)象。
- 返回值是
None
,頁面會(huì)報(bào) 500 狀態(tài)碼錯(cuò)誤,視圖函數(shù)不會(huì)執(zhí)行。 - 返回值是
HttpResponse
對(duì)象,則是對(duì)應(yīng)的響應(yīng)信息,頁面不會(huì)報(bào)錯(cuò)。
中間件中的方法
方法 | 作用 |
---|---|
process_request(self,request) |
在視圖函數(shù)之前執(zhí)行 |
process_view(self, request, view_func, view_args, view_kwargs) |
視圖函數(shù)之前,process_request 方法之后執(zhí)行 |
process_exception(self, request, exception) |
視圖函數(shù)中出現(xiàn)異常了才執(zhí)行 |
process_response(self, request, response) |
視圖函數(shù)之后執(zhí)行 |
下面一圖就能比較好的呈現(xiàn) django
整個(gè)處理流程邏輯
更多的中間件細(xì)節(jié)可以去 Django 官方文檔 進(jìn)行了解。
統(tǒng)一異常處理具體設(shè)計(jì)
結(jié)合自定義的異常和狀態(tài)碼枚舉類,進(jìn)行異常日志信息和業(yè)務(wù)邏輯的處理。
自定義異常模塊
# exceptions.py #!/usr/bin/python3 # -*- coding: utf-8 -*- # @Author: Hui # @Desc: { 項(xiàng)目異常模塊 } # @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 # 狀態(tài)碼枚舉類 super().__init__() class BusinessException(CommonException): """業(yè)務(wù)異常類""" pass class APIException(CommonException): """接口異常類""" pass
自定義狀態(tài)碼枚舉類
# enums.py #!/usr/bin/python3 # -*- coding: utf-8 -*- # @Author: Hui # @Desc: { 項(xiàng)目枚舉類模塊 } # @Date: 2021/09/23 23:37 from enum import Enum class StatusCodeEnum(Enum): """狀態(tài)碼枚舉類""" OK = (0, '成功') ERROR = (-1, '錯(cuò)誤') SERVER_ERR = (500, '服務(wù)器異常') IMAGE_CODE_ERR = (4001, '圖形驗(yàn)證碼錯(cuò)誤') THROTTLING_ERR = (4002, '訪問過于頻繁') NECESSARY_PARAM_ERR = (4003, '缺少必傳參數(shù)') USER_ERR = (4004, '用戶名錯(cuò)誤') PWD_ERR = (4005, '密碼錯(cuò)誤') CPWD_ERR = (4006, '密碼不一致') MOBILE_ERR = (4007, '手機(jī)號(hào)錯(cuò)誤') SMS_CODE_ERR = (4008, '短信驗(yàn)證碼有誤') ALLOW_ERR = (4009, '未勾選協(xié)議') SESSION_ERR = (4010, '用戶未登錄') REGISTER_FAILED_ERR = (4011, '注冊(cè)失敗') DB_ERR = (5000, '數(shù)據(jù)庫錯(cuò)誤') EMAIL_ERR = (5001, '郵箱錯(cuò)誤') TEL_ERR = (5002, '固定電話錯(cuò)誤') NODATA_ERR = (5003, '無數(shù)據(jù)') NEW_PWD_ERR = (5004, '新密碼錯(cuò)誤') OPENID_ERR = (5005, '無效的openid') PARAM_ERR = (5006, '參數(shù)錯(cuò)誤') STOCK_ERR = (5007, '庫存不足') @property def code(self): """獲取狀態(tài)碼""" return self.value[0] @property def errmsg(self): """獲取狀態(tài)碼信息""" return self.value[1]
- 自定義的異常類用于區(qū)分系統(tǒng)異常和業(yè)務(wù)來進(jìn)行單獨(dú)處理。
- 狀態(tài)碼枚舉則是用來記錄對(duì)應(yīng)的異常信息。
狀態(tài)碼枚舉類的設(shè)計(jì)可以查閱 巧用Python 枚舉類設(shè)計(jì)狀態(tài)碼信息
響應(yīng)信息統(tǒng)一結(jié)果的封裝
統(tǒng)一前后端交互數(shù)據(jù)和異常信息結(jié)果。
# result.py #!/usr/bin/python3 # -*- coding: utf-8 -*- # @Author: Hui # @Desc: { 項(xiàng)目信息返回結(jié)果模塊 } # @Date: 2021/09/23 22:10 from .enums import StatusCodeEnum class R(object): """ 統(tǒng)一項(xiàng)目信息返回結(jié)果類 """ def __init__(self): self.code = None self.errmsg = None self._data = dict() @staticmethod def ok(): """ 組織成功響應(yīng)信息 :return: """ r = R() r.code = StatusCodeEnum.OK.code r.errmsg = StatusCodeEnum.OK.errmsg return r @staticmethod def error(): """ 組織錯(cuò)誤響應(yīng)信息 :return: """ r = R() r.code = StatusCodeEnum.ERROR.code r.errmsg = StatusCodeEnum.ERROR.errmsg return r @staticmethod def server_error(): """ 組織服務(wù)器錯(cuò)誤信息 :return: """ r = R() r.code = StatusCodeEnum.SERVER_ERR.code r.errmsg = StatusCodeEnum.SERVER_ERR.errmsg return r @staticmethod def set_result(enum): """ 組織對(duì)應(yīng)枚舉類的響應(yīng)信息 :param enum: 狀態(tài)枚舉類 :return: """ r = R() r.code = enum.code r.errmsg = enum.errmsg return r def data(self, key=None, obj=None): """統(tǒng)一后端返回的數(shù)據(jù)""" if key: self._data[key] = obj context = { 'code': self.code, 'errmsg': self.errmsg, 'data': self._data } return context
完善統(tǒng)一異常處理邏輯
# middlewares.py #!/usr/bin/python3 # -*- coding: utf-8 -*- # @Author: Hui # @Desc: { 項(xiàng)目中間件模塊 } # @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): """統(tǒng)一異常處理中間件""" def process_exception(self, request, exception): """ 統(tǒng)一異常處理 :param request: 請(qǐng)求對(duì)象 :param exception: 異常對(duì)象 :return: """ if isinstance(exception, BusinessException): # 業(yè)務(wù)異常處理 data = R.set_result(exception.enum_cls).data() return JsonResponse(data) elif isinstance(exception, DatabaseError): # 數(shù)據(jù)庫異常 r = R.set_result(StatusCodeEnum.DB_ERR) logger.error(r.data(), exc_info=True) return HttpResponseServerError(StatusCodeEnum.SERVER_ERR.errmsg) elif isinstance(exception, Exception): # 服務(wù)器異常處理 r = R.server_error() logger.error(r.data(), exc_info=True) return HttpResponseServerError(r.errmsg) return None
應(yīng)用場(chǎng)景
注冊(cè)校驗(yàn)
讓我們來看一段注冊(cè)校驗(yàn)功能業(yè)務(wù)邏輯
def verify_params(self, request): """ 校驗(yàn)注冊(cè)信息 :param request: 注冊(cè)請(qǐng)求對(duì)象 :return: response_ret """ # 接受參數(shù) 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('參數(shù)錯(cuò)誤') return response_ret # 用戶名 5-20個(gè)字符 if not re.match(r'^[a-zA-Z0-9_]{5,20}', self.username): response_ret = http.HttpResponseForbidden('用戶名不規(guī)范') return response_ret # 密碼 8-20個(gè)字符 if not re.match(r'^[a-zA-Z0-9]{8,20}', self.password): response_ret = http.HttpResponseForbidden('密碼不規(guī)范') return response_ret # 兩次密碼一致性 if self.password != self.confirm_pwd: response_ret = http.HttpResponseForbidden('兩次密碼不一致') return response_ret # 手機(jī)號(hào)合法性 if not re.match(r'^1[3-9]\d{9}$', self.mobile): response_ret = http.HttpResponseForbidden('手機(jī)號(hào)碼不合法') return response_ret # 是否勾選用戶協(xié)議 if self.allow != 'on': response_ret = http.HttpResponseForbidden('請(qǐng)勾選用戶協(xié)議') return response_ret return response_ret
通過拋異常和設(shè)置狀態(tài)碼枚舉來處理
def verify_params(self, request): """ 校驗(yàn)注冊(cè)信息 :param request: 注冊(cè)請(qǐng)求對(duì)象 :return: response_ret """ # 接受參數(shù) 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') # 校驗(yàn)參數(shù) 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個(gè)字符 if not re.match(r'^[a-zA-Z0-9_]{5,20}', self.username): raise BusinessException(StatusCodeEnum.USER_ERR) # 密碼 8-20個(gè)字符 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) # 手機(jī)號(hào)合法性 if not re.match(r'^1[3-9]\d{9}$', self.mobile): raise BusinessException(StatusCodeEnum.MOBILE_ERR) # 是否勾選用戶協(xié)議 if self.allow != 'on': raise BusinessException(StatusCodeEnum.ALLOW_ERR)
減少 try ... except ...
代碼塊
例如在對(duì)數(shù)據(jù)庫進(jìn)行操作時(shí),為了防止數(shù)據(jù)庫發(fā)生了意外的異常導(dǎo)致系統(tǒng)崩潰,通常加上 try ... except ...
來記錄異常信息。然而配置了全局異常處理,則可以不用管理。
# 創(chuàng)建用戶 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, )
注意:如果需要通過異常捕獲來處理一些業(yè)務(wù)信息,則不可避免,如事務(wù)回滾等
源代碼
可能通過文章方式不好理解其思想,大家可以通過項(xiàng)目源代碼的方式來參考。
美多商城 https://gitee.com/huiDBK/meiduo_project/tree/master
尾語
✍ 用 Code 譜寫世界,讓生活更有趣。❤️
✍ 萬水千山總是情,點(diǎn)贊再走行不行。❤️
✍ 碼字不易,還望各位大俠多多支持。❤️
到此這篇關(guān)于python中通過Django捕獲所有異常的處理的文章就介紹到這了,更多相關(guān)python Django捕獲異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python實(shí)現(xiàn)base64編碼的圖片保存到本地功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)base64編碼的圖片保存到本地功能,涉及Python針對(duì)base64編碼解碼與圖形文件輸出保存相關(guān)操作技巧,需要的朋友可以參考下2018-06-06python實(shí)現(xiàn)報(bào)表自動(dòng)化詳解
這篇文章主要介紹了python實(shí)現(xiàn)報(bào)表自動(dòng)化詳解,涉及python讀,寫excel—xlwt常用功能,xlutils 常用功能,xlwt寫Excel時(shí)公式的應(yīng)用等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11pytorch之深度神經(jīng)網(wǎng)絡(luò)概念全面整理
這篇文章主要介紹了pytorch之深度神經(jīng)網(wǎng)絡(luò)概念,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09django 刪除數(shù)據(jù)庫表后重新同步的方法
今天小編就為大家分享一篇django 刪除數(shù)據(jù)庫表后重新同步的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-05-05tensorflow實(shí)現(xiàn)圖像的裁剪和填充方法
今天小編就為大家分享一篇tensorflow實(shí)現(xiàn)圖像的裁剪和填充方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07