Django源碼分析(四):請求來了

起步

上節咱們瞭解到,WSGI 能夠將請求信息轉發給咱們的應用。python

這一點,原理以及已經請求的處理咱們已經知道了,本節咱們將專一於應用 django 是如何進行處理 WSGI 轉發過來的請求的。django

開始

# django.core.handlers.wsgi.py

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        """請求會被轉發到這裏 """
        # 彷佛是加 `URL` 後綴用的, 暫時不夠清晰
        set_script_prefix(get_script_name(environ))
        # 發送請求來了的信號, 關於信號的註冊及處理咱們在後面的篇章會進行介紹
        signals.request_started.send(sender=self.__class__, environ=environ)
        # 封裝 `request` 請求對象 處理類是 -> WSGIRequest
        request = self.request_class(environ)

        # 劃重點 -> 接下來咱們看它背後發生了什麼? (子類沒有就去父類裏面去找)
        response = self.get_response(request)

        # 下面的待上述講解結束, 客官繼續向下看(代碼已被省略)...
        ...
複製代碼

緊跟着,情節發展到了 get_response 這個方法這裏。app

# django.core.handlers.base.py BaseHandler

class BaseHandler:
    _view_middleware = None
    _template_response_middleware = None
    _exception_middleware = None
    _middleware_chain = None

    def get_response(self, request):
        # 設置 url 解析器, 裏面具體發生了咱們暫且不關心。
        # 這裏爲後續的路由匹配作好了準備
        set_urlconf(settings.ROOT_URLCONF)

        # _middleware_chain 方法是什麼 ...
        # 這裏可能會有一些繞, 這個屬性是在調用 `load_middleware` 這裏賦值
        # 咱們將視線轉移到 `load_middleware` 處 ...
        # ---- 時間線 ----
        # 經過 `load_middleware` 方法咱們知道了 `_middleware_chain` 是個什麼東西。
        # 有多是 `_get_response` 或 某個中間件實例(實例內部有個屬性是 `_get_response` )
        # 假設它是個中間件實例, 請各位繼續移步向下看。
        response = self._middleware_chain(request)

        # 這裏刪除了一些彷佛不過重要的代碼, 也就是說直接響應了 `response` 對象
        ...

        return response

    def load_middleware(self):
        """該方法在 runserver 的時候實例化應用的時候,在 __init__ 方法中調用了該方法 """

        self._view_middleware = []
        self._template_response_middleware = []
        self._exception_middleware = []

        # `convert_exception_to_response` 寫法上是一個裝飾器。
        # 這個方法裏面完成了對響應的一些異常的捕獲
        # 好比 django 40四、500 等頁面提示以及請求異常信號的發送
        # 如今 `handler` 指向 `_get_response` 這個方法
        handler = convert_exception_to_response(self._get_response)

        # 獲取 settings 中間件的配置
        for middleware_path in reversed(settings.MIDDLEWARE):
            # 動態導入模塊
            middleware = import_string(middleware_path)
            try:
                # 注意:這裏實例化的時候, 將 `handler` 傳遞爲了屬性
                mw_instance = middleware(handler)  # 實例化
            except MiddlewareNotUsed as exc:
                # 一些不過重要的代碼被我刪除了..
                ...

            # 塞入中間件實現的方法
            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 被從新賦值,變爲了中間件實例(彷佛是一直不斷的被從新賦值)
            handler = convert_exception_to_response(mw_instance)

        # 這個屬性多是 `__get_response` 方法 或 是一箇中間件實例
        self._middleware_chain = handler
複製代碼

上述咱們瞭解到,經過實現了 wsgi 框架跑起來一個服務的時候,將 django.core.handlers.wsgi.py 下的 WSGIHandler 的實例 設置爲了應用程序。框架

WSGIHandler 實例化的時候,執行了 load_middleware 方法,該方法載入了 django 的中間件,並設置了 _middleware_chain 屬性。(該屬性有多是 _get_response 或 某個中間件的實例(實例中有 _get_response 一個屬性)。)函數

請求被轉發到應用程序 django,也就是 WSGIHandler 下的 __call__,這裏設置了路由解析器,並調用了 _middleware_chain,返回值是一個 response 對象。atom

接下來,咱們看下 _middleware_chain 發生了什麼?url

漸入佳境

既然上文咱們設定 _middleware_chain 是一箇中間件,且是一個類的實例,那麼問題來了?spa

實例加括號調用什麼方法? 答案是類的 __call__ 方法。code

__call__ 方法被定義在了 django.utils.deprecation 下的 MiddlewareMixin。(繼承它的子類並無實現 __call__ 方法。)server

# django.utils.deprecation.py

class MiddlewareMixin:
    def __init__(self, get_response=None):
        # 還記得上文提到的 `get_response` 嗎?
        self.get_response = get_response
        super().__init__()

    def __call__(self, request):
        """ 上文咱們提到的執行 `_middleware_chain`,其實就是執行到了這裏。 """
        response = None
        # 開始處理中間件方法, 請求來了...
        if hasattr(self, 'process_request'):
            # 若是中間件方法返回了 response 對象,至關於截斷了後續的操做。
            response = self.process_request(request)
        # 真正有趣的地方來了, 咱們假設中間件方法沒有攔截,執行了 `get_response` 方法。
        # 請求處理的邏輯就是在這裏了 ... 咱們繼續向下看
        response = response or self.get_response(request)
        # 繼續執行中間件方法
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response
複製代碼

上述主要調用了中間件的 __call__ 方法,在該方法中,執行了中間件相關處理方法,繼續向下剖析。

# django.core.handlers.base.py BaseHandler 下的 `_get_response` 方法

def _get_response(self, request):
    """解析並調用視圖,以及 視圖、異常和模板響應 中間件。 這個方法是 請求/響應 中間件中發生的全部事情。 """
    response = None

    # 這個裏面主要是根據 settings 配置的 url 節點,導入並獲取一個解析器對象。
    if hasattr(request, 'urlconf'):
        urlconf = request.urlconf
        set_urlconf(urlconf)
        resolver = get_resolver(urlconf)
    else:
        resolver = get_resolver()

    # 根據訪問的路由獲取 django 路由解析器中的匹配的視圖
    resolver_match = resolver.resolve(request.path_info)
    callback, callback_args, callback_kwargs = resolver_match
    request.resolver_match = resolver_match

    # 執行含有 `process_view` 方法的中間件
    for middleware_method in self._view_middleware:
        response = middleware_method(request, callback, callback_args, callback_kwargs)
        if response:
            break

    # 若是響應對象並無攔截,執行視圖函數
    if response is None:
        # 保證視圖的原子性,這裏的變量表示視圖
        wrapped_callback = self.make_view_atomic(callback)
        try:
            # django 是視圖寫法有倆種,FBV 和 CBV
            # CBV 會調用 `as_view` 方法,內部實現了 view 函數,而後該函數分發到了類的 `dispatch` 方法,經過反射映射到具體的方法。
            # 其實本質上這裏都是在調用視圖函數。
            # 這一步就是真正在執行您的視圖內的邏輯啦。
            response = wrapped_callback(request, *callback_args, **callback_kwargs)
        except Exception as e:
            # 若是異常,這裏捕獲掉,執行含有 `process_exception` 方法的中間件
            response = self.process_exception_by_middleware(e, request)

    if response is None:
        # 這裏告知你,要必須返回一個 `response` 對象。
        ...

    # 若是上面的視圖中間件返回有 response 對象且可被調用
    elif hasattr(response, 'render') and callable(response.render):
        # 執行含有 `process_template_response` 方法的中間件
        for middleware_method in self._template_response_middleware:
            response = middleware_method(request, response)

        try:
            response = response.render()
        except Exception as e:
            # 若是發生異常,這裏捕獲掉,執行含有 `process_exception` 方法的中間件
            response = self.process_exception_by_middleware(e, request)

    # 響應 response 對象。
    return response

複製代碼

到這裏,經過上述剖析,若是請求進來了,咱們知悉了請求是如何到咱們的視圖邏輯中的。

總結

當咱們的服務跑起來的時候,設置應用處理器 WSGIHandler 實例化的時候加載了中間件的操做。

請求來了,請求被轉發到了 WSGIHandler 下的 __call__ 方法,這裏初始化了請求來了的信號,及將請求信息封裝到了 WSGIRequest 這個對象中。

接着向下執行 get_response 方法主要設置了路由的配置模塊路徑,到了 _middleware_chain 方法這裏,它實際上是一個 _get_response 對象 或是某個中間件的實例(這個實例中有個 get_response 屬性指向 _get_response 這個方法)。這裏咱們假設它是中間件實例,調用的實例的 __call__ 方法(存在於 MiddlewareMixin 下)。這個方法下執行了 process_requestprocess_response 這倆箇中間件,在它們中間執行 _get_response,這個方法會解析路由並調用視圖方法及一些中間件方法。

_get_response 方法下,獲取了路由解析器實例,路由解析器根據請求信息匹配到視圖,並執行視圖方法,獲取到響應結果(若是中間件設置了相關方法,會進行調用)。

相關文章
相關標籤/搜索