Django(五)中间件
一次請求的生命周期
我們先看客戶端發起一次http請求的流程
注:django框架不包括socket, 要借助圖中的兩個模塊來實現socket,進行客戶端和服務端通信。wsgi性能較弱,一般測試開發用;企業中用uwsgi,性能強,并發好。
中間件
從圖上可知,請求在到達視圖之前,會依次執行中間件,視圖返回的響應,依次倒序執行中間件。
在django中,中間件的是一個個的類,類中定義了一些方法。在django項目的setting.py文件中,有一個MIDDLEWARE列表,其中每一個元素都是一個中間件。
如果需要對請求作統一處理,比如在執行視圖前,驗證登錄,日志記錄,我們可以用裝飾器,但是很麻煩,每個視圖都要手動加裝飾器語法。這時如果用中間件會更方便。
中間件的執行原理
從上圖我們發現,MIDDLEWARE列表中的每一個元素都是字符串,那么Django是如何通過字符信息執行的中間件呢。
觀察中間件元素發現由兩部分構成:路徑+類名;因此如果能分離出路徑,并根據路徑導入模塊,然后執行模塊中類的方法,就可以了。
關鍵問題是路徑和類名都是字符串,通過字符串路徑導入模塊要借助importlib模塊;在模塊中通過字符串拿到類和在類中根據字符串拿到方法,都可以通過反射來做。
下面我們來自己模擬一下:
PLUGIN = {'disk': 'plugin.disk.Disk','memory': 'plugin.memory.Memory','network': 'plugin.network.Network', }這里用了字典的形式,原理是一樣的。
import importlib # 通過字符串導入模塊for k, v in PLUGIN.items():# 分離模塊路徑 和 類名:# 'plugin.disk.Disk' --> 模塊路徑'plugin.disk' 類名 'Disk' (結果都是字符串)# 通過字符串路徑導入模塊 importlib# 通過反射獲取模塊中的類module_name, cls_name = v.rsplit('.', maxsplit=1)module = importlib.import_module(module_name)cls = getattr(module, cls_name)obj = cls() # 實例化對象res = obj.method() # 調用對象的綁定方法自定義中間件
下面我們自定義一個中間件,用于用戶驗證:
from django.utils.deprecation import MiddlewareMixin # 導入類class M1(MiddlewareMixin): # 自定義類,繼承MiddlewareMixindef process_request(self, request):print('--- 自定義中間件進行登錄驗證 ---')# 如果訪問路徑是login函數,放行;否則驗證session信息if request.path_info == 'login.html':return Nonefrom django.shortcuts import redirectif not request.session.get('user_info'):return redirect('/login.html')def process_response(self, request, response):print('自定義邏輯')return response注意,新版django可能無法導入,因此,直接把MiddlewareMixin類拿過來繼承就好了:
class MiddlewareMixin(object):def __init__(self, get_response=None):self.get_response = get_responsesuper(MiddlewareMixin, self).__init__()def __call__(self, request):response = Noneif hasattr(self, 'process_request'):response = self.process_request(request)if not response:response = self.get_response(request)if hasattr(self, 'process_response'):response = self.process_response(request, response)return responseclass M1(MiddlewareMixin): # 自定義類,繼承MiddlewareMixin......注意,自定義類時,類中的方法和參數都是固定的。
定義完成后,在setting.py中找到MIDDLEWARE列表,將我們剛剛自定義的中間件路徑添加進去:
這樣,每次請求進來,都會先執行自定義的中間件。
中間件中的方法
- process_request 請求進來時執行;不能有返回值,一旦某個中間件的process_request 有返回值,那么下面的中間件就不走了,后面的url路由,視圖函數更不會執行,直接執行當前中間件的process_response方法,層層往上返回這個返回值(如果process_response返回了其它值,或者上面的中間件返回了其它值,最后執行的返回值會覆蓋之前的返回值。
- process_response 返回響應時執行,默認情況下返回response, 即視圖函數的返回值,如果在這個方法中自定義了返回值,將返回自定義返回值。
- process_view
執行時機:執行完process_request – > 路由分發, – > process_view –> 視圖函數 –> process_response。process_view一旦有返回值,那么跳過下面的中間件,開始從視圖層層往上,返回響應。假設有多個中間件,每個中間件中都定義了以上三個方法,那么django執行中間件的邏輯其實相當于將每個中間件中的一類方法放到一個列表里,一共3個列表,循環列表執行其中的方法,過程如下:
for process_request in process_request_list:process_request()執行路由分發 urlfor process_view in process_view_list:process_view()執行視圖 func()for process_response in process_response_list:process_response()- process_exception 默認不執行;如果視圖中出現異常,就會被執行。應用場景:自定義一個”更友好“的錯誤頁面;
- process_template_response 默認不執行;如果返回的對象有render方法,就會被執行。不常用。
總結
以上是生活随笔為你收集整理的Django(五)中间件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle数据库导入导出命令imp/e
- 下一篇: 2014.03.20入职第四天