Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
An overview of the
Unifying exception handling across your project prevents uncaught exceptions from appearing in your code. This article shows you how to handle unified exceptions in a Django project, combined with a status code enumeration class to log project exception information.
Django has uniform exception handling
In a Django project, you can customize the middleware class to inherit the MiddlewareMixin middleware class from django.middleware.com and override the exception handling logic of the process_exception method. Global exception handling can then be done by registering in the middleware under the project configuration.
I wrote the middleware in the middlewares.py module under the utils package defined by the project.
# middlewares.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @desc: {project middleware module}
# @Date: 2021/09/24 8:18
from django.middleware.common import MiddlewareMixin
class ExceptionMiddleware(MiddlewareMixin) :
""" Unified exception Handling middleware ""
def process_exception(self, request, exception) :
Param Request: request object :param exception: return: """
# exception handling
print(exception)
return None
Copy the code
Here for the time being, simply outputting the exception to simulate the exception processing. Finally, don’t forget to register the middleware in the configuration file. The default configuration file for a Django project is settings.py. I just put the configuration file in the Settings package and changed the file name.
The middlewareprocess_exception
Methods to introduce
The process_exception method is executed only if an exception occurs in the view function. The return value of this method can be either None or an HttpResponse object.
- The return value is
None
The page displays a 500 status code error and the view function will not execute. - The return value is
HttpResponse
Object, is the corresponding response information, the page will not report an error.
Methods in middleware
methods | role |
---|---|
process_request(self,request) |
Execute before the view function |
process_view(self, request, view_func, view_args, view_kwargs) |
Before the view function,process_request Method is executed after |
process_exception(self, request, exception) |
Execute only if an exception occurs in the view function |
process_response(self, request, response) |
The view function is then executed |
The following diagram provides a good illustration of the entire Django process logic
More middleware details can be found in the Official Django documentation.
Unified specific design of exception handling
Combined with the custom exception and status code enumeration classes, the exception log information and business logic processing.
Custom exception module
# exceptions.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @desc: {project exception module}
# @Date: 2021/09/24 8:14
class CommonException(Exception) :
""" Public exception class """
def __init__(self, enum_cls) :
self.code = enum_cls.code
self.errmsg = enum_cls.errmsg
self.enum_cls = enum_cls State code enumeration class
super().__init__()
class BusinessException(CommonException) :
""" Business exception class ""
pass
class APIException(CommonException) :
""" Interface exception class ""
pass
Copy the code
Custom state code enumeration class
# enums.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @desc: {item enumeration class module}
# @Date: 2021/09/23 23:37
from enum import Enum
class StatusCodeEnum(Enum) :
State code enumeration class
OK = (0.'success')
ERROR = (-1.'wrong')
SERVER_ERR = (500.'Server exception')
IMAGE_CODE_ERR = (4001.'Graphic captcha error')
THROTTLING_ERR = (4002.'Visited too often')
NECESSARY_PARAM_ERR = (4003.'Mandatory parameters missing')
USER_ERR = (4004.'Wrong username')
PWD_ERR = (4005.'Password error')
CPWD_ERR = (4006.'Inconsistent passwords')
MOBILE_ERR = (4007.'Wrong phone number')
SMS_CODE_ERR = (4008.'SMS verification code error')
ALLOW_ERR = (4009.'Protocol not selected')
SESSION_ERR = (4010.'User not logged in')
REGISTER_FAILED_ERR = (4011.'Registration failed')
DB_ERR = (5000.'Database error')
EMAIL_ERR = (5001.'Email error')
TEL_ERR = (5002.'Landline error')
NODATA_ERR = (5003.'No data')
NEW_PWD_ERR = (5004.'New password error')
OPENID_ERR = (5005.'Invalid OpenID')
PARAM_ERR = (5006.'Parameter error')
STOCK_ERR = (5007.'Understock')
@property
def code(self) :
""" Get the status code """
return self.value[0]
@property
def errmsg(self) :
""" Obtain status code information ""
return self.value[1]
Copy the code
-
Custom exception classes are used to distinguish system exceptions from business exceptions for separate processing.
-
The status code enumeration is used to record the corresponding exception information.
The design of state code enumeration classes can be seen using Python enumeration classes to design state code information
Encapsulation of unified results for response information
Unified data and exception information between the front and back ends.
# result.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @desc: {project information return result module}
# @Date: 2021/09/23 22:10
from .enums import StatusCodeEnum
class R(object) :
"" unified Project Information return result class ""
def __init__(self) :
self.code = None
self.errmsg = None
self._data = dict(a) @staticmethod
def ok() :
""" Organization successful response message :return: ""
r = R()
r.code = StatusCodeEnum.OK.code
r.errmsg = StatusCodeEnum.OK.errmsg
return r
@staticmethod
def error() :
""" Organization error response message :return: ""
r = R()
r.code = StatusCodeEnum.ERROR.code
r.errmsg = StatusCodeEnum.ERROR.errmsg
return r
@staticmethod
def server_error() :
""" Organization server error message :return: ""
r = R()
r.code = StatusCodeEnum.SERVER_ERR.code
r.errmsg = StatusCodeEnum.SERVER_ERR.errmsg
return r
@staticmethod
def set_result(enum) :
Param enum: status enumeration class :return: """
r = R()
r.code = enum.code
r.errmsg = enum.errmsg
return r
def data(self, key=None, obj=None) :
"" data returned by unified backend ""
if key:
self._data[key] = obj
context = {
'code': self.code,
'errmsg': self.errmsg,
'data': self._data
}
return context
Copy the code
Improve unified exception handling logic
# middlewares.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @desc: {project middleware module}
# @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) :
""" Unified exception Handling middleware ""
def process_exception(self, request, exception) :
Param Request: request object :param exception: return: """
if isinstance(exception, BusinessException):
# Service exception handling
data = R.set_result(exception.enum_cls).data()
return JsonResponse(data)
elif isinstance(exception, DatabaseError):
# database exception
r = R.set_result(StatusCodeEnum.DB_ERR)
logger.error(r.data(), exc_info=True)
return HttpResponseServerError(StatusCodeEnum.SERVER_ERR.errmsg)
elif isinstance(exception, Exception):
Server exception handling
r = R.server_error()
logger.error(r.data(), exc_info=True)
return HttpResponseServerError(r.errmsg)
return None
Copy the code
Application scenarios
Certified check
Let’s look at a piece of business logic for the registration validation function
def verify_params(self, request) :
Param request: return response_ret ""
# accept parameters
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('Parameter error')
return response_ret
The username is 5-20 characters long
if not re.match(R '^ [a zA - Z0-9 _] {5, 20}', self.username):
response_ret = http.HttpResponseForbidden('Wrong username')
return response_ret
# Password 8-20 characters
if not re.match(R '^ [a zA - Z0-9] {8, 20}', self.password):
response_ret = http.HttpResponseForbidden('Bad password')
return response_ret
Password consistency
ifself.password ! = self.confirm_pwd: response_ret = http.HttpResponseForbidden('Two different passwords')
return response_ret
# Phone number validity
if not re.match(r'^1[3-9]\d{9}$', self.mobile):
response_ret = http.HttpResponseForbidden('Mobile phone number is not valid')
return response_ret
# Whether to select user protocol
ifself.allow ! ='on':
response_ret = http.HttpResponseForbidden('Please check user Protocol')
return response_ret
return response_ret
Copy the code
Handle by throwing exceptions and setting the status code enumeration
def verify_params(self, request) :
Param request: return response_ret ""
# accept parameters
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')
# check parameter
all_args = [self.username, self.password, self.confirm_pwd, self.mobile, self.allow]
if not all(all_args):
raise BusinessException(StatusCodeEnum.PARAM_ERR)
The username is 5-20 characters long
if not re.match(R '^ [a zA - Z0-9 _] {5, 20}', self.username):
raise BusinessException(StatusCodeEnum.USER_ERR)
# Password 8-20 characters
if not re.match(R '^ [a zA - Z0-9] {8, 20}', self.password):
raise BusinessException(StatusCodeEnum.PWD_ERR)
Password consistency
ifself.password ! = self.confirm_pwd:raise BusinessException(StatusCodeEnum.CPWD_ERR)
# Phone number validity
if not re.match(r'^1[3-9]\d{9}$', self.mobile):
raise BusinessException(StatusCodeEnum.MOBILE_ERR)
# Whether to select user protocol
ifself.allow ! ='on':
raise BusinessException(StatusCodeEnum.ALLOW_ERR)
Copy the code
To reducetry ... except ...
The code block
For example, when performing operations on a database, a try… is added to prevent the database from crashing due to unexpected exceptions. except … To record exception information. However, if global exception handling is configured, management is not required.
# create user
try:
user = User.objects.create_user(
username=self.username,
password=self.password,
mobile=self.mobile,
)
except DatabaseError as e:
logger.error(e)
# global exception handling
user = User.objects.create_user(
username=self.username,
password=self.password,
mobile=self.mobile,
)
Copy the code
Note: It is inevitable that some business information, such as transaction rollback, needs to be handled through exception catching
The source code
It may be difficult to understand its ideas through the article, we can refer to the project source code.
Beauty shop https://gitee.com/huiDBK/meiduo_project/tree/master
The tail language
✍ Code writes the world and makes life more interesting. ❤ ️
✍ thousands of rivers and mountains always love, ✍ go again. ❤ ️
✍ code word is not easy, but also hope you heroes support. ❤ ️