1. 程式人生 > 實用技巧 >Django中介軟體深入理解

Django中介軟體深入理解

​ 今天在網上搜尋中介軟體方法的時候,看到有些中介軟體並不是按照之前我學習的那幾個鉤子函式來實現的,而是直接寫了一個 __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_requestprocess_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 會在BaseHandlerget_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

之後就實現了一種類似遞迴的呼叫。