Django中介軟體深入理解
阿新 • • 發佈:2020-10-22
今天在網上搜尋中介軟體方法的時候,看到有些中介軟體並不是按照之前我學習的那幾個鉤子函式來實現的,而是直接寫了一個 __init__
和一個__call__
方法來實現的,決定看一下,為什麼這麼實現可以變成一箇中間件。
平常實現方式
一般我們實現我們都是繼承了 MiddlewareMixin
然後實現相關的鉤子函式。
MiddlewareMixin
原始碼
class MiddlewareMixin: # middleware_instance = A(B(C(D(_get_response)))) # self.get_respons = B(C(D(_get_response)) def __init__(self, get_response=None): self.get_response = get_response super().__init__() # 通過__call__實現一種類似遞迴呼叫的方式 def __call__(self, request): response = None if hasattr(self, 'process_request'): # A(B(C(D(_get_response))))(request)會呼叫__call__(request)中的邏輯執行 # 1. A(B(C(D(_get_response)))).process_request(request) 中介軟體A的process_request()方法被執行返回none response = self.process_request(request) if not response: # 2. response為None self.get_response 指的是A的B(C(D(_get_response)),B(C(D(_get_response))(request) 又會去呼叫 # __call__(request) ——> B(C(D(_get_response)).process_request(request) 中介軟體B的process_request()方法被執行返回none # 再次進入if not response分支 C(D(_get_response)(request)直到最後執行 _get_response(request)方法 response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response
這裡的__call__
方法只處理了 process_request
和 process_response
其他的幾個鉤子函式都是在 self.get_response
中處理的
Django啟動的時候
Django啟動的時候,會先例項化 WSGIHander
這個類
class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware()
self.load_middleware
load_middleware(self, is_async=False): """ Populate middleware lists from settings.MIDDLEWARE. Must be called after the environment is fixed (see __call__ in subclasses). """ self._view_middleware = [] self._template_response_middleware = [] self._exception_middleware = [] #django 3.0 會判斷是否是非同步 get_response = self._get_response_async if is_async else self._get_response handler = convert_exception_to_response(get_response) handler_is_async = is_async # 開始遍歷 settings中設定的 中介軟體,反序遍歷 for middleware_path in reversed(settings.MIDDLEWARE): # 中介軟體物件 middleware = import_string(middleware_path) middleware_can_sync = getattr(middleware, 'sync_capable', True) middleware_can_async = getattr(middleware, 'async_capable', False) if not middleware_can_sync and not middleware_can_async: raise RuntimeError( 'Middleware %s must have at least one of ' 'sync_capable/async_capable set to True.' % middleware_path ) elif not handler_is_async and middleware_can_sync: middleware_is_async = False else: middleware_is_async = middleware_can_async try: # Adapt handler, if needed. handler = self.adapt_method_mode( middleware_is_async, handler, handler_is_async, debug=settings.DEBUG, name='middleware %s' % middleware_path, ) # 例項化中介軟體物件 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, self.adapt_method_mode(is_async, mw_instance.process_view), ) if hasattr(mw_instance, 'process_template_response'): self._template_response_middleware.append( self.adapt_method_mode(is_async, mw_instance.process_template_response), ) if hasattr(mw_instance, 'process_exception'): # The exception-handling stack is still always synchronous for # now, so adapt that way. self._exception_middleware.append( self.adapt_method_mode(False, mw_instance.process_exception), ) # 此處可以看成 第一次[中介軟體1(get_response)],迴圈第二次[中介軟體2(中介軟體1(get_response))] 一次類推 handler = convert_exception_to_response(mw_instance) handler_is_async = middleware_is_async # Adapt the top of the stack, if needed. handler = self.adapt_method_mode(is_async, handler, handler_is_async) # We only assign to this when initialization is complete as it is used # as a flag for initialization being complete. # 這個就是 中介軟體2(中介軟體1(get_response)) 套娃 self._middleware_chain = handler
之後 self._middleware_chain
會在BaseHandler
的get_response
方法中被呼叫。
class BaseHandler:
def get_response(self, request):
set_urlconf(settings.ROOT_URLCONF)
# 此函式完成對中介軟體的各個函式呼叫已經檢視函式的呼叫
# 首先依次呼叫中介軟體A,B,C,D的process_request
# 之後呼叫_get_respones()方法,_get_respones()方法又會呼叫在load_middleware()方法中從中介軟體中新增的process_view函式,
# process_template_response和 process_exception函式
# 最後依次呼叫中介軟體的process_response方法
response = self._middleware_chain(request)
response._closable_objects.append(request)
...
return response
def _get_response(self, request):
...
return response
之後就實現了一種類似遞迴的呼叫。