3.1 Djang 對 request 的處理
以本地開發為例,當瀏覽器發起一次請求時,Django 中的 wsgi 創建一個 WSGIHandler 對象處理請求。在
WSGIHandler 對象中初始化環境變量,如果沒有異常,則調用 self.get_response(request)
函數處理請求,返回 response 給 wsgi。
get_response
定義在 django.core.handlers.base.py 文件中,下面是處理流程。
|
|
這張圖能比較好的呈現整個處理流程邏輯.
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