django异常处理中间件
以本地开发为例,当浏览器发起一次请求时,Django 中的 wsgi 创建一个 WSGIHandler 对象处理请求。在
WSGIHandler 对象中初始化环境变量,如果没有异常,则调用self.get_response(request)
函数处理请求,返回 response 给 wsgi。
get_response
定义在 django.core.handlers.base.py 文件中,下面是处理流程。
for middleware_method in self._request_middleware:
response = middleware_method(request)
if response:
break
...
if response is None:
...
for middleware_method in self._view_middleware:
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
break
...
response = wrapped_callback(request, *callback_args, **callback_kwargs)
...
if response is None:
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs)
except Exception as e:
for middleware_method in self._exception_middleware:
response = middleware_method(request, e)
if response:
break
if response is None:
raise
...
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
...
return response
这张图能比较好的呈现整个处理流程逻辑.
3.2 ExceptionBox
Django 的中间件支持一种 Exception 的写法。当发生未捕获处理的异常时,执行中间件中定义的函数process_exception
,如果返回一个 response, 那么就可以结束整个流程。
在 Django 工程中,需要一个异常处理和错误码统一管理的模块。于是便有了 ExceptionBox。
数据的返回格式:
{
'code': '404',
'message': '错误提示XXXX',
'result': False,
'data': None
}
代码实例演示:
简介
django-exceptionbox 是一个 Django 的异常处理工具包。
将 HTTP 状态码封装成 Python 异常 Base 类,使用 raise 抛出异常,通过 Django 中间件,统一处理异常,打印日志。
通过继承 base.py 中的异常类,可以实现对异常的封装。
需要注意的是,返回的错误码是类名,是一个短语。这样处理的目的是为了前后端更易于理解和使用。
下载包之后,需要重命名为 exceptionbox。
配置添加中间件 'exceptionbox.middleware.ExceptionBoxMiddleware' 到 settings中
MIDDLEWARE_CLASSES = (
'exceptionbox.middleware.ExceptionBoxMiddleware',
...
)
中间件的位置没有特殊要求
使用第一步在exceptionbox/error.py
文件,继承base.py
中的异常类,实现业务逻辑相关的异常类。
例如:
class ERROR_LOGIN_FRONT_PAY_NOT_MONEY(base.PreconditionFailed412):第二步
message = "没有足够余额"
在views.py
文件中,抛出异常。
import exceptionbox
def my_view(request):
raise exceptionbox.ERROR_LOGIN_FRONT_PAY_NOT_MONEY()
接口返回
status_code = 412
{
"message": "没有足够余额",
"code": "ERROR_LOGIN_FRONT_PAY_NOT_MONEY",
"data": null,
"result": false
}
base.py
from abc import ABCMeta
class BaseReturn(Exception):
__metaclass__ = ABCMeta
# 1XX Informational
class Continue100(BaseReturn):
status_code = 100
class SwitchingProtocols101(BaseReturn):
status_code = 101
class Processing102(BaseReturn):
status_code = 102
# 2XX Success
class OK200(BaseReturn):
status_code = 200
class Created201(BaseReturn):
status_code = 201
class Accepted202(BaseReturn):
status_code = 202
class NonAuthoritativeInformation203(BaseReturn):
status_code = 203
class NoContent204(BaseReturn):
status_code = 204
class ResetContent205(BaseReturn):
status_code = 205
class PartialContent206(BaseReturn):
status_code = 206
class MultiStatus207(BaseReturn):
status_code = 207
class AlreadyReported208(BaseReturn):
status_code = 208
class IMUsed226(BaseReturn):
status_code = 226
# 3XX Redirection
class MultipleChoices300(BaseReturn):
status_code = 300
class MovedPermanently301(BaseReturn):
status_code = 301
class Found302(BaseReturn):
status_code = 302
class SeeOther303(BaseReturn):
status_code = 303
class NotModified304(BaseReturn):
status_code = 304
class UseProxy305(BaseReturn):
status_code = 305
class TemporaryRedirect307(BaseReturn):
status_code = 307
class PermanentRedirect308(BaseReturn):
status_code = 308
class Accepted202(BaseReturn):
status_code = 202
# 4XX Client Error
class BadRequest400(BaseReturn):
status_code = 400
class Unauthorized401(BaseReturn):
status_code = 401
class PaymentRequired402(BaseReturn):
status_code = 402
class Forbidden403(BaseReturn):
status_code = 403
class NotFound404(BaseReturn):
status_code = 404
class MethodNotAllowed405(BaseReturn):
status_code = 405
class NotAcceptable406(BaseReturn):
status_code = 406
class ProxyAuthenticationRequired407(BaseReturn):
status_code = 407
class RequestTimeout408(BaseReturn):
status_code = 408
class Conflict409(BaseReturn):
status_code = 409
class Gone410(BaseReturn):
status_code = 410
class LengthRequired411(BaseReturn):
status_code = 411
class PreconditionFailed412(BaseReturn):
status_code = 412
class PayloadTooLarge413(BaseReturn):
status_code = 413
class RequestURITooLong414(BaseReturn):
status_code = 414
class UnsupportedMediaType415(BaseReturn):
status_code = 415
class RequestedRangeNotSatisfiable416(BaseReturn):
status_code = 416
class ExpectationFailed417(BaseReturn):
status_code = 417
class IMATeapot418(BaseReturn):
status_code = 418
class MisdirectedRequest421(BaseReturn):
status_code = 421
class UnprocessableEntity422(BaseReturn):
status_code = 422
class Locked423(BaseReturn):
status_code = 423
class FailedDependency424(BaseReturn):
status_code = 424
class UpgradeRequired426(BaseReturn):
status_code = 426
class PreconditionRequired428(BaseReturn):
status_code = 428
class TooManyRequests429(BaseReturn):
status_code = 429
class PreconditionRequired428(BaseReturn):
status_code = 428
class RequestHeaderFieldsTooLarge431(BaseReturn):
status_code = 431
class ConnectionClosedWithoutResponse444(BaseReturn):
status_code = 444
class UnavailableForLegalReasons451(BaseReturn):
status_code = 451
class ClientClosedRequest499(BaseReturn):
status_code = 499
# 5XX Server Error
class InternalServerError500(BaseReturn):
status_code = 500
class NotImplemented501(BaseReturn):
status_code = 501
class BadGateway502(BaseReturn):
status_code = 502
class ServiceUnavailable503(BaseReturn):
status_code = 503
class GatewayTimeout504(BaseReturn):
status_code = 504
class HTTPVersionNotSupported505(BaseReturn):
status_code = 505
class VariantAlsoNegotiates506(BaseReturn):
status_code = 506
class InsufficientStorage507(BaseReturn):
status_code = 507
class LoopDetected508(BaseReturn):
status_code = 508
class NotExtended510(BaseReturn):
status_code = 510
class NetworkAuthenticationRequired511(BaseReturn):
status_code = 511
class NetworkConnectTimeoutError599(BaseReturn):
status_code = 599
© 2021 GitHub, Inc.
error.py
from __future__ import unicode_literals
from . import base
class ERROR_LOGIN_FRONT_NOT_GIFT(base.PreconditionFailed412):
message = u"礼品不充足"
class ERROR_LOGIN_FRONT_PAY_NOT_MONEY(base.PreconditionFailed412):
message = u"没有足够余额"
class ERROR_FAULT(base.ServiceUnavailable503):
message = u"服务器内部错误"
middiareware.py
# -*- coding: utf-8 -*-
import json
import logging
import traceback
from django.http import JsonResponse
from .base import BaseReturn
logger = logging.getLogger('root')
class ExceptionBoxMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_exception(self, request, exception):
if not issubclass(exception.__class__, BaseReturn):
return None
ret_json = {
'code': exception.__class__.__name__,
'message': getattr(exception, 'message', ''),
'success': False,
'data': []
}
response = JsonResponse(ret_json)
response.status_code = getattr(exception, 'status_code', 500)
_logger = logger.error if response.status_code >= 500 else logger.warning
_logger('status_code->{status_code}, error_code->{code}, url->{url}, '
'method->{method}, param->{param}, '
'traceback->{traceback}'.format(
status_code=response.status_code, code=ret_json['code'], url=request.path,
method=request.method, param=json.dumps(getattr(request, request.method, {})),
traceback=traceback.format_exc()
))
return response
源代码地址:middiareware原作者的代码有bug按照我的代码替换
https://github.com/shaowenchen/django-exceptionbox/issues
版权声明
本文仅代表作者观点,不代表博信信息网立场。