目錄
一岩调、中間件簡介
Django官方文檔描述
二、激活中間件
三号枕、中間件包含的鉤子方法和應用的順序
1. 鉤子方法的調用
1.1 process_request
1.2 process_view
1.3 process_template_response
1.4 process_response
1.5 process_exception
四缰揪、編寫自己的中間件
1. 正常流程
2. process_request返回HttpResponse
3. 加入process_view()方法
3.1 將上述中間件代碼中M3.process_request()返回一個HttpResponse(其余代碼不變):
3.2 M2.process_view()返回HttpResponse,M3.process_request()返回None堕澄,其余代碼不變:
4. 加入process_exception()方法
4.1 將上述中間件代碼中的M2.process_response()返回HttpResponse
五邀跃、中間件應用場景
1、做IP限制
2蛙紫、URL訪問過濾
3拍屑、緩存(還記得CDN嗎?)
一、中間件簡介
在django中坑傅,中間件其實就是一個類僵驰,在請求到來和結束后,django會根據自己的規(guī)則在合適的時機執(zhí)行中間件中相應的方法唁毒。
Django官方文檔描述
中間件是一個鉤子框架蒜茴,它們可以介入Django 的請求和響應處理過程。它是一個輕量級浆西、底層的“插件”系統(tǒng)粉私,用于在全局修改Django 的輸入或輸出。
每個中間件組件負責完成某個特定的功能近零。例如诺核,Django 包含的一個中間件組件AuthenticationMiddleware ,它使用會話將用戶和請求關聯起來久信。
這篇文檔講解中間件如何工作窖杀、如何激活中間件以及如何編寫你自己的中間件。Django集成了一些內置的中間件可以直接開箱即用裙士。它們被歸檔在 .內置中間件參考
二入客、激活中間件
要激活一個中間件組件,需要把它添加到Django項目的配置文件settings.py中的MIDDLEWARE列表中腿椎。
在MIDDLEWARE中桌硫,每一個中間件組件用字符串的方式描述:一個完整的Python全路徑加上中間件的類名稱。例如啃炸,使用django-admin startproject
創(chuàng)建項目的時候生成的默認值:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware', # admin界面默認是顯示英語鞍泉,如果要顯示中文,就添加這一行中間件
]
Django的程序中肮帐,中間件不是必需的---只要你喜歡咖驮,MIDDLEWARE列表可以為空边器,但是強烈推薦你至少使用CommonMiddleware這個中間件。
MIDDLEWARE中的順序非常重要托修,因為一個中間件可能依賴于另外一個忘巧。因為一個中間件可能依賴于另外一個。例如睦刃,AuthenticationMiddleware在會話中儲存已認證的用戶砚嘴。所以它必須在SessionMiddleware之后運行。一些關于Django中間件類的順序的常見提示涩拙,請見中間件排序际长。
三、中間件包含的鉤子方法和應用的順序
在請求階段中兴泥,調用視圖之前工育,Django會按照MIDDLEWARE中定義的順序自頂向下應用中間件。會用到兩個鉤子:
process_request(request)
process_view(request, view_func, view_args, view_kwargs)
在響應階段中搓彻,調用視圖之后如绸,中間件會按照相反的順序應用,自底向上旭贬。會用到三個鉤子:
process_exception(request, exception
process_template_response(request, response)
process_response(request, response)
上面5種鉤子方法怔接,你可以在自己寫的中間件類中定義其中一種或多種。
1. 鉤子方法的調用
1.1 process_request
process_request(request)
request是一個HttpRequest 對象稀轨。
Django在執(zhí)行視圖函數前扼脐,先從上往下調用每個中間件的process_request()方法。它應該返回一個None或一個HttpResponse對象奋刽。如果返回None(不寫返回值相當于返回None)瓦侮,Django會繼續(xù)調用下一個中間件的process_request()方法,當所有中間件的process_request()方法全部調用完畢且沒有返回HttpResponse對象時杨名,再從第一個中間件開始依次調用每個中間件的process_view()方法,最后執(zhí)行對應的視圖函數猖毫。如果它返回一個HttpResponse對象台谍,Django就不用再去調用其它中間件的process_request()、process_view()或process_exception()和視圖函數了吁断,它將對HttpResponse 運用響應階段的中間件趁蕊,并返回結果。
1.2 process_view
process_view(request, view_func, view_args, view_kwargs)
request是一個HttpRequest對象仔役。view_func是個視圖函數掷伙, view_args是一個會被傳遞到視圖的位置參數列表,而view_kwargs 是一個會被傳遞到視圖的關鍵字參數字典又兵。 view_args和 view_kwargs 都不包括第一個視圖參數(request)任柜。
process_view()會在Django 調用視圖之前被調用卒废。
它將返回None 或一個HttpResponse 對象。如果返回None宙地,Django 將會繼續(xù)執(zhí)行其它的process_view() 中間件摔认,然后調用對應的視圖。如果返回一個HttpResponse對象宅粥,Django 就不用再去調用其它的view 或exception 中間件参袱,或對應的視圖;它將對HttpResponse 運用響應階段的中間件秽梅,并返回結果抹蚀。
注意:在中間件內部,從process_request 或process_view 中訪問request.POST 或request.REQUEST 將阻礙該中間件之后的所有視圖無法修改請求的上傳處理程序企垦,一般情況下要避免這樣使用环壤。
類CsrfViewMiddleware可以被認為是個例外,因為它提供csrf_exempt() 和csrf_protect()兩個裝飾器竹观,允許視圖顯式控制在哪個點需要開啟CSRF驗證镐捧。
1.3 process_template_response
這個鉤子實際中使用的較少
process_template_response(request, response)
request是一個HttpRequest對象。response是一個TemplateResponse對象(或等價的對象)臭增,由Django視圖或者中間件返回懂酱。
如果響應的實例有render()方法,process_template_response()在視圖剛好執(zhí)行完畢之后被調用誊抛,這表明了它是一個TemplateResponse對象(或等價的對象)列牺。
這個方法必須返回一個實現了render方法的響應對象。它可以修改給定的response對象拗窃,通過修改 response.template_name和response.context_data或者它可以創(chuàng)建一個全新的 TemplateResponse或等價的對象瞎领。
你不需要顯式渲染響應 —— 一旦所有的模板響應中間件被調用,響應會自動被渲染随夸。
在一個響應的處理期間九默,中間件以相反的順序運行,這包括process_template_response()宾毒。
1.4 process_response
process_response(request, response)
process_response()在所有響應返回瀏覽器之前被調用驼修。
request是一個HttpRequest對象。response是Django視圖或者中間件返回的HttpResponse或者StreamingHttpResponse對象诈铛。
這個方法必須返回HttpResponse或者StreamingHttpResponse對象乙各。它可以改變已有的response,或者創(chuàng)建并返回新的HttpResponse或StreamingHttpResponse對象幢竹。
不像 process_request()和process_view()方法耳峦,即使同一個中間件類中的process_request()和process_view()方法會因為前面的一個中間件返回HttpResponse而被跳過,process_response()方法總是會被調用焕毫。特別是蹲坷,這意味著你的process_response()方法不能依賴于process_request()方法中的設置驶乾。
注意:最后,記住在響應階段中冠句,中間件以相反的順序被應用轻掩,自底向上。意思是定義在MIDDLEWARE最底下的類的process_response()會最先被運行懦底。
1.5 process_exception
process_exception(request, exception)
當一個視圖拋出異常時唇牧,Django會調用process_exception()來處理。
request是一個HttpRequest對象聚唐。exception是一個被視圖中的方法拋出來的 Exception對象丐重。
process_exception()應該返回一個None 或者一個HttpResponse對象。如果它返回一個HttpResponse對象杆查,模型響應和響應中間件會被應用扮惦,響應結果會返回給瀏覽器。否則亲桦, 默認的異常處理機制將會被觸發(fā)崖蜜。
再次提醒,在處理響應期間客峭,中間件的執(zhí)行順序是倒序執(zhí)行的豫领,這包括process_exception。如果一個異常處理的中間件返回了一個響應舔琅,那這個中間件上面的中間件都將不會被調用等恐。
四、編寫自己的中間件
Django版本
>> django-admin version
1.11.7
自己寫的中間件(類)必須繼承MiddlewareMixin
1. 正常流程
只有process_request和process_response時
from django.shortcuts import HttpResponse
from django.utils.deprecation import MiddlewareMixin
class M1(MiddlewareMixin):
def process_request(self,request):
print('中間件: M1.process_request')
def process_response(self,request,response):
print('中間件:M1.process_response')
return response
class M2(MiddlewareMixin):
def process_request(self,request):
print('中間件:M2.process_request')
def process_response(self,request,response):
print('中間件:M2.process_response')
return response
class M3(MiddlewareMixin):
def process_request(self,request):
print('中間件:M3.process_request')
def process_response(self,request,response):
print('中間件:M3.process_response')
return response
在settings.py中注冊自己編寫的中間件:
MIDDLEWARE = [
...
'middleware.testMiddleWare.M1',
'middleware.testMiddleWare.M2',
'middleware.testMiddleWare.M3',
...
]
寫一個簡單的視圖test
def test(request):
print('views.test')
return HttpResponse('ok')
urls.py中添加一條url
urlpatterns = [
...
url(r'test/', views.test),
]
訪問http://127.0.0.1:8000/test/后备蚓,后端打印結果如下:
中間件: M1.process_request
中間件:M2.process_request
中間件:M3.process_request
視圖函數:views.test
中間件:M3.process_response
中間件:M2.process_response
中間件:M1.process_response
從輸出結果课蔬,可以得出執(zhí)行流程如下圖:
2. process_request返回HttpResponse
將上述中間件代碼中的M2.process_request()返回HttpResponse(其余代碼不變):
class M2(MiddlewareMixin):
def process_request(self,request):
print('中間件:M2.process_request')
return HttpResponse('前端顯示:中間件:M2.process_request')
def process_response(self,request,response):
print('中間件:M2.process_response')
return response
前端顯示結果:
前端顯示:中間件:M2.process_request
后端輸入結果:
中間件: M1.process_request
中間件:M2.process_request
中間件:M2.process_response
中間件:M1.process_response
從輸出結果,可以得出執(zhí)行流程如下圖:
M2.process_request()返回HttpResponse后郊尝,不再執(zhí)行后面中間件的process_request()方法和視圖函數二跋,直接依次往上(倒序)執(zhí)行M2和M1的process_response()方法。
3. 加入process_view()方法
from django.shortcuts import HttpResponse
from django.utils.deprecation import MiddlewareMixin
class M1(MiddlewareMixin):
def process_request(self,request):
print('中間件: M1.process_request')
def process_response(self,request,response):
print('中間件:M1.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M1.process_view')
class M2(MiddlewareMixin):
def process_request(self,request):
print('中間件:M2.process_request')
def process_response(self,request,response):
print('中間件:M2.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M2.process_view')
print('callback:',callback)
class M3(MiddlewareMixin):
def process_request(self,request):
print('中間件:M3.process_request')
def process_response(self,request,response):
print('中間件:M3.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M3.process_view')
前端顯示視圖函數test返回的:ok
后端輸出結果如下:
中間件: M1.process_request
中間件:M2.process_request
中間件:M3.process_request
中間件:M1.process_view
中間件:M2.process_view
callback: <function test at 0x00000000045A8A60>
中間件:M3.process_view
視圖函數:views.test
中間件:M3.process_response
中間件:M2.process_response
中間件:M1.process_response
依結果得出執(zhí)行流程如下:
3.1 將上述中間件代碼中M3.process_request()返回一個HttpResponse(其余代碼不變):
class M3(MiddlewareMixin):
def process_request(self,request):
print('中間件:M3.process_request')
return HttpResponse('前端顯示:中間件:M3.process_request')
def process_response(self,request,response):
print('中間件:M3.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M3.process_view')
前端頁面顯示:
前端顯示:中間件:M3.process_request
后端輸出結果如下:
中間件: M1.process_request
中間件:M2.process_request
中間件:M3.process_request
中間件:M3.process_response
中間件:M2.process_response
中間件:M1.process_response
從輸入結果流昏,可以得到如下執(zhí)行流程:
從上述結果中可以看出扎即,當process_request()返回HttpResponse時,就不再執(zhí)行process_view()了横缔。process_view()的callback參數是被執(zhí)行的視圖函數對象铺遂。
3.2 M2.process_view()返回HttpResponse衫哥,M3.process_request()返回None茎刚,其余代碼不變:
注意:函數沒有返回值時,默認返回None撤逢。
class M2(MiddlewareMixin):
def process_request(self,request):
print('中間件:M2.process_request')
def process_response(self,request,response):
print('中間件:M2.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M2.process_view')
return HttpResponse('前端顯示:中間件:M2.process_view')
前端頁面顯示:
前端顯示:中間件:M2.process_view
后端輸出結果如下:
中間件: M1.process_request
中間件:M2.process_request
中間件:M3.process_request
中間件:M1.process_view
中間件:M2.process_view
中間件:M3.process_response
中間件:M2.process_response
中間件:M1.process_response
從上述結果可以得出如下執(zhí)行流程:
從上圖中可以看出膛锭,當中間某一個中間件的process_view()返回HttpResponse時粮坞,就不再執(zhí)行下面中間件的process_view()和視圖函數了。
4. 加入process_exception()方法
此方法是當視圖函數中拋出異常時執(zhí)行初狰,當返回None時莫杈,前端頁面會拋出一個異常頁面,當返回HttpResponse時奢入,前端頁面會顯示你定制的內容筝闹。
from django.shortcuts import HttpResponse
from django.utils.deprecation import MiddlewareMixin
class M1(MiddlewareMixin):
def process_request(self,request):
print('中間件: M1.process_request')
def process_response(self,request,response):
print('中間件:M1.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M1.process_view')
# return HttpResponse('M1.process_view')
def process_exception(self,request,exception):
print('中間件:M1.process_exception')
class M2(MiddlewareMixin):
def process_request(self,request):
print('中間件:M2.process_request')
# return HttpResponse('前端顯示:中間件:M2.process_request')
def process_response(self,request,response):
print('中間件:M2.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M2.process_view')
# return HttpResponse('前端顯示:中間件:M2.process_view')
def process_exception(self,request,exception):
print('中間件:M2.process_exception')
class M3(MiddlewareMixin):
def process_request(self,request):
print('中間件:M3.process_request')
# return HttpResponse('前端顯示:中間件:M3.process_request')
def process_response(self,request,response):
print('中間件:M3.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M3.process_view')
# return HttpResponse('前端顯示:中間件:M3.process_view')
def process_exception(self,request,exception):
print('中間件:M3.process_exception')
故意給視圖函數test加一個錯誤:
def test(request):
int('abc') #將字母轉換成整型一定會拋出異常
print('視圖函數:views.test')
return HttpResponse('ok')
前端頁面顯示如下圖:
4.1 將上述中間件代碼中的M2.process_response()返回HttpResponse
class M2(MiddlewareMixin):
def process_request(self,request):
print('中間件:M2.process_request')
def process_response(self,request,response):
print('中間件:M2.process_response')
return response
def process_view(self,request,callback,args,kwargs):
print('中間件:M2.process_view')
def process_exception(self,request,exception):
print('中間件:M2.process_exception')
return HttpResponse("500錯誤")
前面頁面顯示:
500錯誤
后端輸出結果:
中間件: M1.process_request
中間件:M2.process_request
中間件:M3.process_request
中間件:M1.process_view
中間件:M2.process_view
中間件:M3.process_view
中間件:M3.process_exception
中間件:M2.process_exception
中間件:M3.process_response
中間件:M2.process_response
中間件:M1.process_response
執(zhí)行流程如下圖:
從上圖可以看出,當中一個中間件的process_exception()方法捕獲到視圖函數中拋出異常的后并返回HttpResponse腥光,則前端頁面就顯示你自定制的內容关顷。后端程序也不會出現異常。并且前面的中間件process_exception()也不再執(zhí)行武福。
五议双、中間件應用場景
由于中間件工作在 視圖函數執(zhí)行前、執(zhí)行后(像不像所有視圖函數的裝飾器W狡)適合所有的請求/一部分請求做批量處理
1平痰、做IP限制
放在 中間件類的列表中,阻止某些IP訪問了伍纫;
2宗雇、URL訪問過濾
如果用戶訪問的是login視圖(放過)
如果訪問其他視圖(需要檢測是不是有session已經有了放行,沒有返回login)翻斟,這樣就省得在 多個視圖函數上寫裝飾器了逾礁!
3、緩存(還記得CDN嗎?)
客戶端請求來了访惜,中間件去緩存看看有沒有數據嘹履,有直接返回給用戶,沒有再去邏輯層 執(zhí)行視圖函數