上節咱們瞭解到,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_request
和 process_response
這倆箇中間件,在它們中間執行 _get_response
,這個方法會解析路由並調用視圖方法及一些中間件方法。
_get_response
方法下,獲取了路由解析器實例,路由解析器根據請求信息匹配到視圖,並執行視圖方法,獲取到響應結果(若是中間件設置了相關方法,會進行調用)。