第23天,Django之中間件

目錄

一岩调、中間件簡介
    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í)行流程如下圖:


執(zhí)行流程1

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í)行流程如下圖:

執(zhí)行流程2

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í)行流程如下:

執(zhí)行流程3

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í)行流程:


執(zhí)行流程4

從上述結果中可以看出扎即,當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í)行流程:

執(zhí)行流程5

從上圖中可以看出膛锭,當中間某一個中間件的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í)行流程如下圖:


執(zhí)行流程6

從上圖可以看出,當中一個中間件的process_exception()方法捕獲到視圖函數中拋出異常的后并返回HttpResponse腥光,則前端頁面就顯示你自定制的內容关顷。后端程序也不會出現異常。并且前面的中間件process_exception()也不再執(zhí)行武福。

五议双、中間件應用場景

由于中間件工作在 視圖函數執(zhí)行前、執(zhí)行后(像不像所有視圖函數的裝飾器W狡)適合所有的請求/一部分請求做批量處理

1平痰、做IP限制

放在 中間件類的列表中,阻止某些IP訪問了伍纫;

2宗雇、URL訪問過濾

如果用戶訪問的是login視圖(放過)

如果訪問其他視圖(需要檢測是不是有session已經有了放行,沒有返回login)翻斟,這樣就省得在 多個視圖函數上寫裝飾器了逾礁!

3、緩存(還記得CDN嗎?)

客戶端請求來了访惜,中間件去緩存看看有沒有數據嘹履,有直接返回給用戶,沒有再去邏輯層 執(zhí)行視圖函數

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末债热,一起剝皮案震驚了整個濱河市砾嫉,隨后出現的幾起案子,更是在濱河造成了極大的恐慌窒篱,老刑警劉巖焕刮,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異墙杯,居然都是意外死亡配并,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門高镐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溉旋,“玉大人,你說我怎么就攤上這事嫉髓」劾埃” “怎么了邑闲?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長梧油。 經常有香客問我苫耸,道長,這世上最難降的妖魔是什么儡陨? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任褪子,我火速辦了婚禮,結果婚禮上骗村,老公的妹妹穿的比我還像新娘褐筛。我一直安慰自己,他們只是感情好叙身,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布渔扎。 她就那樣靜靜地躺著,像睡著了一般信轿。 火紅的嫁衣襯著肌膚如雪晃痴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天财忽,我揣著相機與錄音倘核,去河邊找鬼。 笑死即彪,一個胖子當著我的面吹牛紧唱,可吹牛的內容都是我干的。 我是一名探鬼主播隶校,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼漏益,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了深胳?” 一聲冷哼從身側響起绰疤,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舞终,沒想到半個月后轻庆,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡敛劝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年余爆,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夸盟。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛾方,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情转捕,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布唆垃,位于F島的核電站五芝,受9級特大地震影響,放射性物質發(fā)生泄漏辕万。R本人自食惡果不足惜枢步,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望渐尿。 院中可真熱鬧醉途,春花似錦、人聲如沸砖茸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凉夯。三九已至货葬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間劲够,已是汗流浹背震桶。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留征绎,地道東北人蹲姐。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像人柿,于是被迫代替她去往敵國和親柴墩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容

  • Refer to: www.threemeal.com/blog/12/ 中間件 中間件是一個鉤子框架凫岖,它們可以介...
    蘭山小亭閱讀 16,504評論 9 165
  • Django 文檔協(xié)作翻譯小組人手緊缺拐邪,有興趣的朋友可以加入我們,完全公益性質隘截。交流群:467338606網站:h...
    布客飛龍閱讀 782評論 0 37
  • 中間件是一個鉤子框架扎阶,它們可以介入Django 的請求和響應處理過程。它是一個輕量級婶芭、底層的“插件”系統(tǒng)东臀,用于在全...
    低吟淺唱1990閱讀 517評論 0 0
  • Django中的中間件是一個輕量級、底層的插件系統(tǒng)犀农,可以介入Django的請求和響應處理過程惰赋,修改Django的輸...
    mlj0503閱讀 419評論 0 0
  • 1,定義 Django中的中間件是一個輕量級、底層的插件系統(tǒng)赁濒,可以介入Django的請求和響應處理過程轨奄,修改Dja...
    曉可加油閱讀 298評論 0 1