zoukankan      html  css  js  c++  java
  • 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
    
    

    之后就实现了一种类似递归的调用。

    图一

  • 相关阅读:
    58) Gitlab加入LDAP认证 (windows AD)
    57) 《乌合之众》读书笔记【1】
    56) 监控系统简单介绍
    前端学习建议汇总(留着自己看的心灵鸡汤)
    vscode分享代码插件Polacode
    PHP论坛实现积分系统的思路
    thinkphp删除图片的方法实现
    php高并发问题解决思路
    PHP和Thinkphp模拟留言板,应对XSS攻击(超完整!)
    sql server特殊字符查询问题及ESCAPE的使用
  • 原文地址:https://www.cnblogs.com/Stay-J/p/13858144.html
Copyright © 2011-2022 走看看