Django中間件部分源碼分析

中間件源碼分析

中間件簡介

中間件是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局範圍內改變Django的輸入和輸出。每一箇中間件組件都負責作一些特定的功能。python

可是因爲其影響的是全局,因此須要謹慎使用,使用不當會影響性能。django

中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。app

中間件中主要能夠定義下面5個鉤子函數來對請求的輸入輸出作處理:框架

  • process_request(self,request)
  • process_view(self, request, view_func, view_args, view_kwargs)
  • process_template_response(self,request,response)
  • process_exception(self, request, exception)
  • process_response(self, request, response)

它們的主要做用參見官方文檔.函數

這5個鉤子函數的觸發時機能夠見下面的圖.源碼分析

說明: 上面說的順序都是中間件在settings文件中列表的註冊順序.性能

源碼分析

我對中間件如何會作出上面的處理順序, 比較好奇, 因而就去研究了下Django的源碼.學習

首先. Django在啓動初始化一系列環境配置, 包括wsgi協議的實現, 也包括中間件組件的初始化. 中間件的初始化入口函數在這.atom

進入到load_middleware函數, 能夠看咱們能夠自定義的鉤子函數都在這裏了, 放在5個列表裏面. 接下來判斷settings裏面的MIDDLEWARE配置項是否爲空, 爲空的話會去django.conf.global_settings.py裏面的默認的配置文件加載中間件.默認的中間件只有下面兩個.url

# django.conf.global_settings.py
MIDDLEWARE_CLASSES = [
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
]

MIDDLEWARE = None

通常咱們都不會註釋掉項目下的7個默認中間件, 因此上面的代碼會走else分支, 這裏的else分支是初始化中間件組件的核心邏輯.最後會把全部的中間件放到self._middleware_chain這個中間件處理鏈之中. 這裏的設計思想我也是花了好一段時間纔想明白.

接下來展開else代碼塊, 到了核心部分. 下面列出的else裏面的源碼部分.

else:
            # 這裏是將handler賦初始值爲self._get_response, 這個函數是用來匹配請求url與調用視圖函數
            # 並應用view, exception, template_response中間件.
            handler = convert_exception_to_response(self._get_response)
            # 接下來一段代碼比較難理解, 但確是設計的精髓.
            # 首先, 遍歷咱們配置的中間件列表, 只不過這裏是逆序遍歷, 至於爲何, 往下看就知道了
            for middleware_path in reversed(settings.MIDDLEWARE):
                # 這裏是將咱們配置的字符串形式的中間件類經過反射解析成類. 原理最後會簡單分析
                middleware = import_string(middleware_path)
                try:
                    # 將中間件類實例化爲一個對象, 這裏把上一個handler當作參數
                    # 這也是可以將中間件經過一個初始化對象鏈式調用的精髓. 下面會有解釋
                    mw_instance = middleware(handler)
                except MiddlewareNotUsed as exc:
                    if settings.DEBUG:
                        if six.text_type(exc):
                            logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
                        else:
                            logger.debug('MiddlewareNotUsed: %r', middleware_path)
                    continue

                # 實例化對象爲None, 由於中間件還能夠是函數形式
                if mw_instance is None:
                    raise ImproperlyConfigured(
                        'Middleware factory %s returned None.' % middleware_path
                    )
                # 將process_view方法添加到_view_middleware列表的開頭
                if hasattr(mw_instance, 'process_view'):
                    self._view_middleware.insert(0, mw_instance.process_view)
                # 將process_template_response方法添加到_template_response_middleware列表的末尾
                # 這裏也能解釋爲何處理模板以及下面的異常時逆序(按照註冊順序逆序)處理的
                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變量
                handler = convert_exception_to_response(mw_instance)

        # 最後這個handler指向的是中間件列表的第一個實例對象
        self._middleware_chain = handler

這樣看完以後上面的分析以後應該仍是難以理解思路, 這須要看這個中間件實例化對象的定義形式, 看下面這個中間件類定義部分;

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__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

每一箇中間件類都有兩個基本方法, 初始化時會保存下一個get_response對象, 最後調用中間件實例化對象就可以不停的調用存儲的get_response對象, 這個是可以實現鏈式調用的關鍵. 上面的思想看下圖演示.

這時候再來看上面的代碼, 起始的handler首先指向最裏層的get_response方法, 而後從列表最後的中間件開始遍歷, 把handler(此時是get_response)當作參數, 生成一箇中間件對象CommonMiddleware, 此時handler指向了這個新的對象, 而後依次循環, 重複上面的操做, 至關於一層包着一層.

最後handler會指向最外層的中間件對象. 而後賦值給self._middleware_chain這個變量.

當咱們調用self._middleware_chain(request)方法的時候, 就會觸發這個中間件的__call__方法. 這個時候從最外層中間件進行, 執行process_request方法, 只要不產生response, 就會一直調用內層的中間件變量, 觸發__call__方法, 一直到最裏層, 開始處理視圖相關的功能. 在url匹配以後, 調用視圖函數以前, 會遍歷全部中間件的process_view方法. 若是返回的結果爲None, 則去調用咱們書寫的視圖函數, 若是觸發異常, 則會遍歷處理全部process_exception方法, 若是沒有則去調用符合條件的process_template_response方法. 觸發異常一樣會觸發process_exception方法. 最後會把結果返回回去. 而這時候會從最裏層一層層往外返回. 這就可以解釋中間件鉤子函數的觸發順序.

這裏再放一個最裏層的處理邏輯, 有一些刪減

# django/core/handlers/base.py
    def _get_response(self, request):
        response = None
        # 路由匹配
        if hasattr(request, 'urlconf'):
            urlconf = request.urlconf
            set_urlconf(urlconf)
            resolver = get_resolver(urlconf)
        else:
            resolver = get_resolver()
        resolver_match = resolver.resolve(request.path_info)
        # 這個callback就是咱們的視圖函數, 後兩個是視圖函數可能須要的參數
        callback, callback_args, callback_kwargs = resolver_match
        request.resolver_match = resolver_match

        # 應用 view middleware 中間件
        for middleware_method in self._view_middleware:
            response = middleware_method(request, callback, callback_args, callback_kwargs)
            # 只要有response返回, 就馬上中止遍歷
            if response:
                break

        if response is None:
            # 給視圖函數包裝一層
            wrapped_callback = self.make_view_atomic(callback)
            try:
                # 這裏是調用視圖函數
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
            except Exception as e:
                # 有異常就進入exception view處理
                response = self.process_exception_by_middleware(e, request)
                
        # 這個不經常使用的process_template_response功能, 看源碼能夠清楚的知道爲何
        # 返回的結果爲啥須要有render方法了
        elif hasattr(response, 'render') and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = middleware_method(request, response)
                #  ...
            try:
                response = response.render()
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
        return response

小結

有了上面的部分源碼分析, 最後能夠明白中間件爲何會是以這樣的順序處理請求和響應的. Django這種設計思想是我重來沒接觸過的, 學習完以後感觸很是深, 只能感慨別人程序的設計之精妙, 本身還要好好學習.

相關文章
相關標籤/搜索