django中間件
一、中間件詳細分析
1.django配置文件settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
... ...
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'pay_gateway.process_handle_middleware.ProcessHandleMiddleware'
]
2.自定義中間件middleware.py
from django.utils.deprecation import MiddlewareMixin
class ProcessHandleMiddleware(MiddlewareMixin):
"""
以下幾個函數的順序,剛好也是中間件的執行順序,
按需在適當的位置做操作
"""
def __init__(self, *args, **kwargs):
"""
如果需要額外初始化數據則定義
"""
super(ProcessHandleMiddleware, self).__init__(*args, **kwargs)
self.start_time = time.time()
def process_request(self, request):
"""request請求一進來的預處理函數。
- 在Django接收到request之後,未解析URL到對應視圖函數之前。Django向它傳入相應的Request對象,以便在方法中修改。
- 若返回None,Django將繼續處理這個request,執行後續的中間件, 然後調用相應的 view。
- 若返回HttpResponse對象,Django將不再執行任何除了process_response(返回瀏覽器的最後一層干預)以外其它的中間件
以及相應的view,Django將立即返回該HttpResponse
- 這裏如果報錯,後邊所有的中間件都就不執行了,直接返回(由內部對error處理,報500)
"""
print("request arrive ...")
def process_view(self, request, callback, callback_args, callback_kwargs):
"""進view前的預處理函數
- 在Django執行完request預處理,路由解析完畢,確定待執行的view函數(即callback函數)之後,但在view實際執行之前
:param request: HttpRequest 對象
:param callback: Django將調用的處理request的python函數. 這是實際的函數對象本身, 而不是字符串表述的函數名
:param callback_args: 將傳入view的位置參數列表,但不包括request參數(它通常是傳入view的第一個參數)
:param callback_kwargs: 將傳入view的關鍵字參數字典
:return:
- 如果返回None, Django將繼續處理這個request ,執行後續的中間件, 然後調用相應的view
- 如果返回HttpResponse對象,Django將不再執行任何其它的中間件(不論種類)以及相應的view,Django會立即返回
- 這裏如果報錯,後邊所有的中間件都就不執行了,直接返回(由內部對error處理,報500)
"""
print("before view")
def process_template_response(self):
"""template模板渲染函數
- 默認不執行,只有在view函數返回的結果對象中有render方法纔會執行
- 若返回的話,會把對象內執行render方法後的返回值返回給用戶(不返回view返回的對象,而是其對象內render方法的返回值)
"""
print("template response ")
def process_exception(self, request, exception):
"""只有view函數處理拋出的錯誤才能接到,其他的錯誤,這裏並不能捕獲
- 可做異常通知,錯誤日誌蒐集,或嘗試從錯誤中自動恢復
- 若返回None,Django將用框架內置的異常處理機制繼續處理相應request
- 若返回HttpResponse對象,Django將使用該response對象,而短路框架內置的異常處理機制,直接返回(其他異常處理便不再執行)
"""
print("process exception")
def process_response(self, request, response):
"""在view函數返回response對象之後,馬上要返回給用戶之前,對其進行二次處理
- 如:用gzip壓縮返回數據
- 這裏必須要返回HttpResponse對象!!! (可以是view返回的,也可是全新的,也可是處理過的,但必須是HttpResponse對象)
"""
print("process response ... ... ")
return response
3.注意事項:
-
process_view
在settings.py
中的MIDDLEWARE
中,後放的先執行,其他過程是先放的先執行。
-
process_response
一定要有reurn否則會報錯,自定義的中間件response方法沒有return,會交給下一個中間件,導致http請求中斷了。
-
process_view(self, request, callback,callback_args, callback_kwargs)
方法介紹:
- (1)執行完所有中間件的
request
方法
- (2)url匹配成功
- (3)拿到視圖函數的名稱、參數(注意不執行),再執行
process_view()
方法(傳遞view函數以及相關參數,但是還是不執行)
- (4)最後去執行視圖函數
4.看圖分析
二、簡化版,以及參數分析
class LoggerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print("start request".center(50, "*"))
print(request)
print(type(request))
print(dir(request))
print(request.method)
print(request.content_type)
print(request.META)
print(request.META['REMOTE_ADDR'])
print(type(request.META['REMOTE_ADDR']))
print(request.content_params)
print(request.encoding)
print(request.body)
print(str(request.body, encoding="utf-8"))
print(request.get_full_path())
print(request.get_host())
print(request.scheme)
print(request.GET)
print(type(request.GET))
print(dir(request.GET))
print(request.GET.dict())
print(request.POST)
print(type(request.POST))
print(dir(request.POST))
print(request.POST.dict())
print(request.POST.items())
print(request.POST.lists())
for k, v in request.POST.items():
print("%s=%s"%(k,v))
print(request.POST.keys())
response = self.get_response(request)
print("get response data".center(50, ">"))
print(type(response))
print(dir(response))
print(response)
print(response.status_code)
print(response.content)
print(response.items())
print(response.readable())
print(response.serialize())
return response
三、應用實例
from django.http.response import JsonResponse
class ServerException(Exception):
def __init__(self, reason, *args):
self.reason = reason
self.code = error_dict[self.reason]['code']
self.data = None
self.msg = error_dict[self.reason]["msg"] % args
def to_response(self):
return JsonResponse({
"code": self.code,
"reason": self.reason,
"data": self.data,
"msg": self.msg
})
class ProcessHandleMiddleware(MiddlewareMixin):
def __init__(self, *args, **kwargs):
super(ProcessHandleMiddleware, self).__init__(*args, **kwargs)
self.start_time = time.time()
def process_request(self, request):
"""
- 這裏不能調用Response進行數據返回!
- Response或SimpleTemplateResponse等實際返回的是Response().render(),
而如果想進行渲染,那麼在實例化時需要指定各種渲染類。
但Response中是沒有渲染類的默認值,而在View中會取默認值給Response,
因而經過view的可以返回,自己在此位置直接調用並返回是不可以。
- HTTPResponse和JSONResponse是直接拼湊二進制數據的響應,不存在渲染操作,所以可以直接返回
"""
def process_exception(self, request, exception):
capture_exception()
if isinstance(exception, ServerException):
return exception.to_response()
def process_response(self, request, response):
"""
1.如果想實現參數封裝,這裏只能使用rest_framework.response.Response !!!
2.只針對接口函數的返回值做封裝+日誌記錄
"""
if str(request.path).startswith("/open-api"):
if hasattr(response, 'data') and isinstance(response, Response):
data = response.data
response.data = {
'code': 0,
'data': data,
'reason': "SUCCESS",
'msg': '成功'
}
response._is_rendered = False
response.render()
req_data = ""
if request.method == 'GET':
req_data = request.GET.dict()
elif request.method == 'POST':
req_data = str(request.body, encoding='utf-8')
resp = ""
if hasattr(response, "content"):
resp = response.content.decode("utf-8")
end_time = time.time()
log_info = "<[REQUEST MESSAGE]>: [req_path]: %s, [mehtod]: %s, [cost_time]: %.2f s, [status]: %s, [req_data]: %s, [resp_data]: %s" % (
request.path,
request.method,
end_time - self.start_time,
response.status_code,
req_data,
resp
)
logging.info(log_info)
return response