django middleware

參考:
Django 源碼學習(3)——中間件
Django Middleware官方文檔
Django==2.0.4源碼

一蹋凝、相關(guān)代碼分布

middleware加載:
django.core.handlers.base.BaseHandlerload_middleware()
運行時:
django.utils.deprecation.MiddlewareMixin铣揉。
django.core.handlers.exception.convert_exception_to_response
django.core.handlers.base.BaseHandlerget_response()

二、源碼分析

中間件的操作對象是BaseHandler_middleware_chain屬性召耘。

    def load_middleware(self):
        """
        Populate middleware lists from settings.MIDDLEWARE.

        Must be called after the environment is fixed (see __call__ in subclasses).
        """
        self._request_middleware = []
        self._view_middleware = []
        self._template_response_middleware = []
        self._response_middleware = []
        self._exception_middleware = []

        handler = convert_exception_to_response(self._get_response)
        for middleware_path in reversed(settings.MIDDLEWARE):
            middleware = import_string(middleware_path)
            try:
                mw_instance = middleware(handler)
            except MiddlewareNotUsed as exc:
                if settings.DEBUG:
                    if str(exc):
                        logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
                    else:
                        logger.debug('MiddlewareNotUsed: %r', middleware_path)
                continue

            if mw_instance is None:
                raise ImproperlyConfigured(
                    'Middleware factory %s returned None.' % middleware_path
                )

            if hasattr(mw_instance, 'process_view'):
                self._view_middleware.insert(0, mw_instance.process_view)
            if hasattr(mw_instance, 'process_template_response'):
                self._template_response_middleware.append(mw_instance.process_template_response)
            if hasattr(mw_instance, 'process_exception'):
                self._exception_middleware.append(mw_instance.process_exception)

            handler = convert_exception_to_response(mw_instance)

        # We only assign to this when initialization is complete as it is used
        # as a flag for initialization being complete.
        self._middleware_chain = handler

1、 convert_exception_to_response的作用

convert_exception_to_response(get_response)函數(shù)可用作裝飾器褐隆,不過污它,在方法load_middleware()中是直接調(diào)用。
該函數(shù)接受一個可調(diào)用對象get_response作為入?yún)⑹饕饔檬前烧{(diào)用對象get_response衫贬,當調(diào)用get_response拋出異常時,將異常轉(zhuǎn)換為response歇攻,該response的status_code為4xx或500祥山。
該函數(shù)自動作用于每個middleware(見上述代碼中的兩次調(diào)用),確保每個middleware都不會拋出異常掉伏,從而使得棧中的上一個middleware收到的是一個response對象(為什么是上一個見下文)缝呕,而不是exception對象。
上述內(nèi)容大體上是該函數(shù)的help文檔斧散,如下:

def convert_exception_to_response(get_response):
    """
    Wrap the given get_response callable in exception-to-response conversion.

    All exceptions will be converted. All known 4xx exceptions (Http404,
    PermissionDenied, MultiPartParserError, SuspiciousOperation) will be
    converted to the appropriate response, and all other exceptions will be
    converted to 500 responses.

    This decorator is automatically applied to all middleware to ensure that
    no middleware leaks an exception and that the next middleware in the stack
    can rely on getting a response instead of an exception.
    """
    @wraps(get_response)
    def inner(request):
        try:
            response = get_response(request)
        except Exception as exc:
            response = response_for_exception(request, exc)
        return response
    return inner

2供常、 _middleware_chain的實質(zhì)

接著分析load_middleware()方法,其大體思路是使用convert_exception_to_response將各個middleware包裹起來鸡捐,然后通過中間變量handler來串聯(lián)栈暇,最后賦值給_middleware_chain屬性。
所以該屬性名副其實箍镜。
變量handler初始化使用的是方法_get_response源祈。

3煎源、 排序

在方法load_middleware中,對三類middleware作了處理香缺,分別是:_view_middleware手销,_template_response_middleware_exception_middleware图张。
其中锋拖,_view_middleware是按照settings.MIDDLEWARE的正序排列,其余二者為逆序祸轮。這些列表里兽埃,都被插入了middleware相對應(yīng)的方法對象。
_middleware_chain屬性由于是經(jīng)過一次次包裹而來适袜,按照代碼邏輯柄错,也是正序。

另外苦酱,_request_middleware_response_middleware應(yīng)該是已被廢棄售貌。

那么,這些屬性間有什么關(guān)聯(lián)躏啰?

4、當一個Request進入Django后

當一個Request進入Django后耙册,會調(diào)用BaseHandlerget_response(request)方法给僵。
該方法調(diào)用_middleware_chain屬性:
response = self._middleware_chain(request)
middleware依照順序挨個調(diào)用,如果之前的middleware一直沒有返回response對象详拙,那么就會進入最后被包裹的可調(diào)用對象:BaseHandler_get_response(request)帝际。
在該方法中,會依次嘗試調(diào)用_view_middleware中的middleware的方法饶辙,_template_response_middleware的處理也類似蹲诀。如果在其中某一步拋出了異常,那么調(diào)用 _exception_middleware中的middleware的方法弃揽。

從上述可見脯爪,如果沒有返回response的話,每個middleware都會被調(diào)用矿微。如果能夠走到BaseHandlerget_response(request)方法痕慢,才會有機會使用_view_middleware_template_response_middleware涌矢、_exception_middleware掖举。
由于上述三個列表里面是相應(yīng)middleware的方法,所以只有擁有對應(yīng)方法的middleware才會走到娜庇。同時塔次,一旦有返回對象方篮,立即停止繼續(xù)調(diào)用下一個列表中的方法,即“短路”励负。

三藕溅、middleware的定義

按理來說應(yīng)該先明白middleware才好理解上述的內(nèi)容,但是我個人基本上是按照這樣一個過程了解過來的熄守,所以這部分就到最后了蜈垮。

大體上學習官方文檔。

1裕照、什么是middleware

middleware是Django的一套置于Request/Response之間的鉤子框架攒发。
middleware是可調(diào)用的(callable),傳入一個Request晋南,返回一個Response惠猿,和view一樣。

middleware可以通過工廠函數(shù)生成负间,也可以定義為一個callable Class

class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

middleware__init__(self, get_response)方法必須有一個入?yún)?em>get_response偶妖,該入?yún)⑹峭瑯邮强烧{(diào)用的。由之前的分析可知政溃,其實就是下一個包裹好的middleware趾访,這樣才能將各個middleware關(guān)聯(lián)起來。具體代碼是load_middleware()方法中的這段:

        handler = convert_exception_to_response(self._get_response)
        for middleware_path in reversed(settings.MIDDLEWARE):
            middleware = import_string(middleware_path)
            try:
                mw_instance = middleware(handler)

middleware__call__(self, request)方法必須有一個入?yún)?em>request董虱。在該方法中扼鞋,通過使用self.get_response(request)來調(diào)用下一個被包裹的middleware,這樣調(diào)用時才會依次調(diào)用愤诱。

2云头、Django提供的middleware Mixin

django.utils.deprecation中:

class MiddlewareMixin:
    def __init__(self, get_response=None):
        self.get_response = get_response
        super().__init__()

    def __call__(self, request):
        response = None
        if 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 response

django.contrib.auth.middleware.AuthenticationMiddleware就是使用的它(Django自帶的基本都用它),而whitenoise.middleware.WhiteNoiseMiddleware就是自己定義的淫半。

大體的過程是溃槐,先調(diào)用process_request方法嘗試獲取response,如果沒有則通過get_response調(diào)用鏈上的下一個middleware科吭,如果其中一個middleware返回了response昏滴,開始調(diào)用process_response來對response處理,不會去找鏈上的下一個middleware对人,也就是采用了“短路”(short-circuit)原則影涉。

關(guān)于短路,文檔中使用洋蔥作為比喻:

During the request phase, before calling the view, Django applies middleware in the order it’s defined in MIDDLEWARE, top-down.
You can think of it like an onion: each middleware class is a “l(fā)ayer” that wraps the view, which is in the core of the onion. If the request passes through all the layers of the onion (each one calls get_response to pass the request in to the next layer), all the way to the view at the core, the response will then pass through every layer (in reverse order) on the way back out.
If one of the layers decides to short-circuit and return a response without ever calling its get_response, none of the layers of the onion inside that layer (including the view) will see the request or the response. The response will only return through the same layers that the request passed in through.

至于view规伐、template蟹倾、exception怎么處理,之前已經(jīng)談到過了。
所以說鲜棠,一個middleware中肌厨,可定義以下幾個方法:process_requestprocess_response豁陆, process_view柑爸,process_template_responseprocess_exception盒音”眵ⅲ可少不可多。

3祥诽、middleware的變遷

new:MIDDLEWARE
old:MIDDLEWARE_CLASSES
主要優(yōu)化了兩點:
第一譬圣,對response的處理更加短路化。
第二雄坪,調(diào)整了exception的處理厘熟,處理的地方變了(從上述內(nèi)容可知,convert_exception_to_response可處理维哈,_exception_middleware也處理)绳姨,處理的結(jié)果也變了。
詳情還是看文檔吧阔挠。

四飘庄、最后

如果看不懂,到源碼里調(diào)試一下就懂了购撼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跪削,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子份招,更是在濱河造成了極大的恐慌切揭,老刑警劉巖狞甚,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锁摔,死亡現(xiàn)場離奇詭異,居然都是意外死亡哼审,警方通過查閱死者的電腦和手機谐腰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涩盾,“玉大人十气,你說我怎么就攤上這事〈夯簦” “怎么了砸西?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我芹枷,道長衅疙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任鸳慈,我火速辦了婚禮饱溢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘走芋。我一直安慰自己绩郎,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布翁逞。 她就那樣靜靜地躺著肋杖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪熄攘。 梳的紋絲不亂的頭發(fā)上兽愤,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音挪圾,去河邊找鬼浅萧。 笑死,一個胖子當著我的面吹牛哲思,可吹牛的內(nèi)容都是我干的洼畅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼棚赔,長吁一口氣:“原來是場噩夢啊……” “哼帝簇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起靠益,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤丧肴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胧后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芋浮,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年壳快,在試婚紗的時候發(fā)現(xiàn)自己被綠了纸巷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡眶痰,死狀恐怖瘤旨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竖伯,我是刑警寧澤存哲,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布撞蚕,位于F島的核電站钦勘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜晃洒,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一稠曼、第九天 我趴在偏房一處隱蔽的房頂上張望僻族。 院中可真熱鬧博杖,春花似錦、人聲如沸氛赐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽艰管。三九已至滓侍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牲芋,已是汗流浹背撩笆。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缸浦,地道東北人夕冲。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像裂逐,于是被迫代替她去往敵國和親歹鱼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

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

  • 在閱讀資料的時候卜高,經(jīng)常見到資料上說弥姻,django處理請求流程的時候,是先middleware處理掺涛,如果沒有返回re...
    llicety閱讀 2,510評論 0 4
  • Django Middleware 這里說的是 Django 1.8.7 的中間件官方文檔在這里 Middlewa...
    raku閱讀 170評論 0 0
  • 中間件是一個鉤子框架庭敦,它們可以介入Django 的請求和響應(yīng)處理過程。它是一個輕量級薪缆、底層的“插件”系統(tǒng)秧廉,用于在全...
    低吟淺唱1990閱讀 517評論 0 0
  • 假設(shè)我們有如下中間件: setting.py文件 Django中間件的五個方法調(diào)用順序如下: process_re...
    naralv閱讀 829評論 0 3
  • django 從請求到返回都經(jīng)歷了什么 從runserver說起 ruserver是使用django自己的web ...
    星丶雲(yún)閱讀 327評論 1 1