Django中間件看完這篇徹底明白

我們在使用python的一些庫時嚼隘,會遇到中間件這個概念诽里,比如scrapy和Django,那么什么是中間件呢飞蛹?

什么是中間件

中間件就是在目標(biāo)結(jié)果之間進(jìn)行的額外處理過程谤狡,在Django中就是request和response之間進(jìn)行的處理,相對來說實現(xiàn)起來比較簡單卧檐,但是要注意它是對全局有效的墓懂,可以在全局范圍內(nèi)改變輸入和輸出結(jié)果,因此需要謹(jǐn)慎使用霉囚,否則不僅會造成難以定位的錯誤捕仔,而且可能會影響整體性能。

中間件有什么用

如果想要修改HttpRequest或者HttpResponse盈罐,就可以通過中間件來實現(xiàn)榜跌。

  • 登陸認(rèn)證:在中間件中加入登陸認(rèn)證,所有請求就自動擁有登陸認(rèn)證盅粪,如果需要放開部分路由钓葫,只需要特殊處理就可以了。
  • 流量統(tǒng)計:可以針對一些渲染頁面統(tǒng)計訪問流量票顾。
  • 惡意請求攔截:統(tǒng)計IP請求次數(shù)础浮,可以進(jìn)行頻次限制或者封禁IP。

中間件執(zhí)行流程

在Django中自定義中間件是非常簡單的奠骄,在settings.py中有一個配置項:

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',
]

只要把添加的中間件配置在這里就可以了豆同。每一個中間件都是一個類,多個中間件可以寫在同一個文件含鳞,也可以在獨立文件中影锈。每個中間件可以包含五個方法:

process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)

我在網(wǎng)上找到這么一張圖片,說明了請求的數(shù)據(jù)流在Django中間件當(dāng)中的執(zhí)行流程

image.png

中間件函數(shù)執(zhí)行流程

  • 請求到達(dá)中間件后先依次執(zhí)行每個中間件的process_request函數(shù)
  • 然后再依次執(zhí)行每個中間件的process_view函數(shù),找到我們的視圖函數(shù)
  • 執(zhí)行視圖函數(shù)處理請求數(shù)據(jù)
  • 如果在上面的過程中出現(xiàn)異常精居,則依次反方向執(zhí)行每個中間件的process_exception函數(shù)
  • 如果請求包含模板渲染锄禽,則依次反方向執(zhí)行每個中間件的process_template_response函數(shù)
  • 最后依次反方向執(zhí)行每個中間件的process_response函數(shù)

以上這些執(zhí)行函數(shù)將返回None或者HttpResponse對象,如果返回None靴姿,則交給下一個中間件的對應(yīng)函數(shù)處理沃但;如果返回HttpResponse對象,則將其返回給用戶

在這些中間件的執(zhí)行函數(shù)中佛吓,我們最常用的就是process_request和process_response函數(shù)宵晚,通常用來在視圖函數(shù)處理前和視圖函數(shù)處理后執(zhí)行一些相應(yīng)的操作,這個要根據(jù)我們的業(yè)務(wù)需求维雇,選擇不同的處理過程淤刃。例如:進(jìn)行登陸認(rèn)證,因為必須要在視圖函數(shù)處理前進(jìn)行認(rèn)證吱型,我們可以在process_request中處理逸贾;攜帶認(rèn)證cookies信息,就可以在process_response函數(shù)中給response對象增加指定cookies值津滞。

中間件回調(diào)函數(shù)執(zhí)行

  • Request函數(shù):process_request(self, request) 執(zhí)行時機:當(dāng)接收到前端請求铝侵,并生成request對象,但是仍未解析url触徐,未確定當(dāng)前要運行的視圖函數(shù)咪鲜。 如果返回None,Django將繼續(xù)處理下一個中間件的request函數(shù)撞鹉;如果返回HttpResponse對象疟丙,Django將不再執(zhí)行其他除process_response以外的所有函數(shù),包括后面的process_request函數(shù)鸟雏、其他中間件函數(shù)以及視圖函數(shù)享郊。
  • View函數(shù):process_view(self, request, callback, callback_args, callback_kwargs) 執(zhí)行時機:在執(zhí)行完所有中間件的process_request函數(shù),并且已經(jīng)匹配到要執(zhí)行的視圖函數(shù)崔慧,但是還沒有調(diào)用視圖函數(shù)之前拂蝎。 callback:要執(zhí)行的視圖函數(shù)對象(就是我們所寫的視圖處理函數(shù)) callback_args:視圖函數(shù)的位置參數(shù)列表(不包含self和request) callback_kwargs:視圖函數(shù)的關(guān)鍵字參數(shù) 如果返回None,Django將繼續(xù)處理下一個中間件的request函數(shù)惶室;如果返回HttpResponse對象,Django將不再執(zhí)行其他除process_response以外的所有函數(shù)玄货,包括后面的process_request函數(shù)皇钞、其他中間件函數(shù)以及視圖函數(shù)
  • Template函數(shù):process_template_response(self, request, response) 執(zhí)行時機:只有在視圖函數(shù)的返回對象中有render方法才會執(zhí)行松捉,并把render方法的返回值返回給用戶夹界。
  • Exception函數(shù):process_exception(self, request, exception) 執(zhí)行時機:如果在執(zhí)行過程中出現(xiàn)問題,并且拋出一個未被捕獲的異常時才被調(diào)用隘世。我們可以用它來捕獲請求錯誤可柿,發(fā)送通知或者恢復(fù)錯誤場景鸠踪。 如果返回None,Django將使用框架內(nèi)置異常處理复斥,并繼續(xù)交給下一個exception函數(shù)营密;如果返回HttpResponse對象,Django將不再執(zhí)行其他除process_response以外的所有函數(shù)目锭,并中斷異常處理评汰。
  • Response函數(shù):process_response(self, request, response) 執(zhí)行時機:執(zhí)行完view函數(shù)并生成response之后,幾乎是必執(zhí)行的函數(shù)痢虹。 返回并且只能被去、必須返回HttpResponse對象,否則會導(dǎo)致HTTP請求中斷奖唯。

自定義中間件

  • 創(chuàng)建中間件類
from django.utils.deprecation import MiddlewareMixin

class MyCustomMiddleware1(MiddlewareMixin):
    def process_request(self, request):
        print('MyCustomMiddleware1')

    def process_response(self, request, response):
        print('返回 MyCustomMiddleware1')
        return response

class MyCustomMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('MyCustomMiddleware2')

    def process_response(self, request, response):
        print('返回 MyCustomMiddleware2')
        return response
  • 注冊中間件
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',
    'MyMiddleware.MyCustomMiddleware1',
    'MyMiddleware.MyCustomMiddleware2'
]

輸出結(jié)果:

MyCustomMiddleware1
MyCustomMiddleware2
返回 MyCustomMiddleware2
返回 MyCustomMiddleware1

系統(tǒng)中間件的用途

  • django.middleware.security.SecurityMiddleware 主要是針對安全訪問處理惨缆,就是把http請求重定向到https請求
  • django.contrib.sessions.middleware.SessionMiddleware 在Django中我們用的request.session就是在process_request中進(jìn)行處理的,根據(jù)我們在settings中配置的SESSION_COOKIE_NAME變量丰捷,從cookies中獲取對應(yīng)的值坯墨,從表中查詢出session值,創(chuàng)建session對象瓢阴,賦值給request_session對象畅蹂。
def process_request(self, request):
    session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
    request.session = self.SessionStore(session_key)

process_response函數(shù)中,給response對象設(shè)置SESSION_COOKIE_NAME值和過期時間等荣恐。

response.set_cookie(
   settings.SESSION_COOKIE_NAME,
   request.session.session_key, max_age=max_age,
   expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
   path=settings.SESSION_COOKIE_PATH,
   secure=settings.SESSION_COOKIE_SECURE or None,
   httponly=settings.SESSION_COOKIE_HTTPONLY or None,
   samesite=settings.SESSION_COOKIE_SAMESITE
)
  • django.middleware.common.CommonMiddleware 檢測是否允許瀏覽器類型
if 'HTTP_USER_AGENT' in request.META:
    for user_agent_regex in settings.DISALLOWED_USER_AGENTS:
        if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
            raise PermissionDenied('Forbidden user agent')

檢查是否需要添加/液斜,主要是根據(jù)settings中APPEND_SLASH配置

if self.should_redirect_with_slash(request):
    path = self.get_full_path_with_slash(request)
else:
    path = request.get_full_path()

在process_response函數(shù)中,會判斷是否需要把404的請求重新定向到我們需要的頁面

  • django.middleware.csrf.CsrfViewMiddleware 這個很明顯就是我們Django框架的csrf驗證了叠穆,主要是process_view中的處理少漆,從函數(shù)處理我們可以看到以下幾點:
    • request請求中包含csrf_processing_done屬性,則不進(jìn)行csrf驗證
    • 視圖函數(shù)中包含csrf_exempt屬性硼被,則不進(jìn)行csrf驗證
    • 如果是GET示损、HEAD、OPTIONS嚷硫、TRACE請求检访,則不進(jìn)行csrf驗證
    • request請求中包含_dont_enforce_csrf_checks屬性,則不進(jìn)行csrf驗證
    • https請求頭中如果不包含HTTP_REFERER仔掸,則拒絕訪問
    • 請求頭中不包含CSRF_COOKIE脆贵,則拒絕訪問
    • POST請求中攜帶csrfmiddlewaretoken參數(shù),如果驗證通過就可以訪問
    • PUT/DELETE請求頭中攜帶CSRF_HEADER_NAME配置起暮,如果驗證通過就可以訪問
  • django.contrib.auth.middleware.AuthenticationMiddleware 這個中間件中為我們的request對象添加了user屬性卖氨,主要是獲取session中SESSION_KEY值(settings配置中),從用戶表中查詢對應(yīng)主鍵,得到用戶對象筒捺,將其付給request.user
  • django.contrib.messages.middleware.MessageMiddleware Django的消息框架柏腻,主要是向目標(biāo)中推送消息內(nèi)容,在前端可通過以下方式使用

https://cloud.tencent.com/developer/article/1631561

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末系吭,一起剝皮案震驚了整個濱河市五嫂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌村斟,老刑警劉巖贫导,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蟆盹,居然都是意外死亡孩灯,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門逾滥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峰档,“玉大人,你說我怎么就攤上這事寨昙〖パ玻” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵舔哪,是天一觀的道長欢顷。 經(jīng)常有香客問我,道長捉蚤,這世上最難降的妖魔是什么抬驴? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮缆巧,結(jié)果婚禮上布持,老公的妹妹穿的比我還像新娘。我一直安慰自己陕悬,他們只是感情好题暖,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捉超,像睡著了一般胧卤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拼岳,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天灌侣,我揣著相機與錄音,去河邊找鬼裂问。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的堪簿。 我是一名探鬼主播痊乾,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼椭更!你這毒婦竟也來了哪审?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤虑瀑,失蹤者是張志新(化名)和其女友劉穎湿滓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舌狗,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡叽奥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了痛侍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朝氓。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖主届,靈堂內(nèi)的尸體忽然破棺而出赵哲,到底是詐尸還是另有隱情,我是刑警寧澤君丁,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布枫夺,位于F島的核電站,受9級特大地震影響绘闷,放射性物質(zhì)發(fā)生泄漏橡庞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一簸喂、第九天 我趴在偏房一處隱蔽的房頂上張望毙死。 院中可真熱鬧,春花似錦喻鳄、人聲如沸扼倘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽再菊。三九已至,卻和暖如春颜曾,著一層夾襖步出監(jiān)牢的瞬間纠拔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工泛豪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留稠诲,地道東北人侦鹏。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像臀叙,于是被迫代替她去往敵國和親略水。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 分析Django的生命周期,我們知道所有的http請求都要經(jīng)過Django的中間件. 假如現(xiàn)在有一個需求,所有到達(dá)...
    汪菲宇閱讀 433評論 0 0
  • 中間件 中間件介紹 什么是中間件 官方的說法是:中間件是一個用來處理Django的請求和相應(yīng)的框架級別的鉤子劝萤。他是...
    可笑的黑耀斑閱讀 377評論 0 0
  • 中間件是 Django 用來處理請求和響應(yīng)的鉤子框架渊涝。它是一個輕量級的、底層級的“插件”系統(tǒng)床嫌,用于全局性地控制Dj...
    liujiangblog閱讀 1,281評論 0 4
  • 1跨释、中間件是一個用來處理Django的請求和響應(yīng)的框架級別的鉤子。它是一個輕量厌处、低級別的插件系統(tǒng)鳖谈,用于在全局范圍內(nèi)...
    SkTj閱讀 3,321評論 0 6
  • 中間件 什么是中間件 中間件就是一個用來全局的處理請求和響應(yīng)的框架級別的鉤子.它本質(zhì)上是一個類.說的直白一點就是中...