您的位置:首页 > 编程语言 > Go语言

Django系列之中间件加载原理

2019-07-02 19:28 330 查看

假设我们有如下中间件:

setting.py文件

MIDDLEWARE = [
'django.middleware.A',
'django.middleware.B',
'django.middleware.C',
'django.middleware.D',
]

Django中间件的五个方法调用顺序如下:

  • process_request(request)

    process_view(request, view_func, args, kwargs)

    if has exception
    process_exception(request, e)
  • process_template_response(request, response)

      if has exception
      process_exception(request, e)
  • process_response(request, response)

  • 需要注意的是,

    process_response
    之前的4个方法中返回了任何的HTTPResponse对象,都不会触发
    process_response

    Django的中间件都必须继承

    MiddlewareMixin

    class MiddlewareMixin:
    
    def __init__(self, get_response=None):
    self.get_response = get_response
    super().__init__()
    
    def __call__(self, request):
    response = None
    if hasattr(self, 'process_request'):
    response = self.process_request(request)
    if not response:
    response = self.get_response(request)
    if hasattr(self, 'process_response'):
    response = self.process_response(request, response)
    return response

    父类的

    __call__()
    方法中只处理了中间件的
    process_request
    process_response
    这个两个一头一尾的方法,中间的几个方法其实都封装在了
    get_response
    这个变量中。

    在Django启动时会初始化

    WSGIHandler
    类,会把
    A
    B
    C
    D
    四个中间件的对象实例化出来

    class WSGIHandler(base.BaseHandler):
    ...
    def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    
    # 加载中间件实例到内存中,此时中间件实例的各种方法已经被包裹在 _get_response() 方法的前后了
    self.load_middleware()
    
    # __call__方法会在一个新的子线程中被调用
    def __call__(self, environ, start_response):
    ...
    request = self.request_class(environ)
    # get_response是父类的方法,调用此方法触发self._middleware_chain()的递归调用
    response = self.get_response(request)
    ..
    return response

    中间件的加载由父类

    BaseHandler
    load_middleware()
    方法实现

    首先看

    load_middleware()
    方法:

    class BaseHandler:
    ...
    def load_middleware(self):
    
    self._request_middleware = []
    self._view_middleware = []
    self._template_response_middleware = []
    self._response_middleware = []
    self._exception_middleware = []
    
    # 这里可以直接把handler看做是self._get_response方法
    handler = convert_exception_to_response(self._get_response)
    # 通过for循环,按照逆序依次实例化要加载的中间件
    for middleware_path in reversed(settings.MIDDLEWARE):
    # 通过中间件的类全命加载他们的类对象
    middleware = import_string(middleware_path)
    try:
    # 然后把handler作为参数,实例化中间件
    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
    )
    
    # 把含有'process_view'方法的中间件对象放入到self._view_middleware列表[]中
    if hasattr(mw_instance, 'process_view'):
    self._view_middleware.insert(0, mw_instance.process_view)
    # 把含有'process_template_response'方法的中间件对象放入到self._template_response_middleware列表[]中
    if hasattr(mw_instance, 'process_template_response'):
    self._template_response_middleware.append(mw_instance.process_template_response)
    # 把含有'process_exception'方法的中间件对象放入到self._exception_middleware列表[]中
    if hasattr(mw_instance, 'process_exception'):
    self._exception_middleware.append(mw_instance.process_exception)
    # 这里的hanler可以直接看做是mw_instance,即一个中间件实例对象
    handler = convert_exception_to_response(mw_instance)
    
    # We only assign to this when initialization is complete as it is used
    # as a flag for initialization being complete.
    # 循环结束时handler是第一个中间件实例对象A(B(C(D(_get_response))))
    self._middleware_chain = handler

    可以从for循环开始分析,看看

    A
    B
    C
    D
    这4个中间件时如何被实例化加载到内存中的

    第一次:

    • handler = _get_response
    • 实例化中间件
      D(handler)
    • 依次查看D中是否有
      process_view
      process_exception
      process_template_response
      有就放入响应的列表中去。
    • 赋值
      handler = D(_get_response)

    第二次:

    • handler = D(_get_response)
    • 实例化中间件
      C(handler)
    • 依次查看C中是否有
      process_view
      process_exception
      process_template_response
      有就放入响应的列表中去。
    • 赋值
      handler = C(D(_get_response))

    第三次:

    • handler = C(D(_get_response))
    • 实例化中间件
      B(handler)
    • 依次查看B中是否有
      process_view
      process_exception
      process_template_response
      有就放入响应的列表中去。
    • 赋值
      handler = B(C(D(_get_response)))

    第四次:

    • handler = B(C(D(_get_response)))
    • 实例化中间件
      A(handler)
    • 依次查看A中是否有
      process_view
      process_exception
      process_template_response
      有就放入响应的列表中去。
    • 赋值
      handler = A(B(C(D(_get_response))))

    到此为止for循环结束,

    handler = A(B(C(D(_get_response))))
    ,也就是这四个中间件的实例都加载到内存了,同时把他们的
    process_view
    process_exception
    process_template_response
    这3个方法分别放到了对应的3个列表中。最后
    self._middleware_chain = handler
    ,也就是把
    handler
    赋值给了
    WSGIHandler
    对象的实例属性
    self._middleware_chain
    。然后
    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

    self._middleware_chain(request)
    被调用时,即
    A(B(C(D(_get_response))))(request)

    • A-CALL-1
      response = A.process_request(request)
    • A-CALL-2 response is None,
      response = B(C(D(_get_response)))(request)
      B-CALL-1
      response = B.process_request(request)
    • B-CALL-2 response is None,
      response = C(D(_get_response))(request)
      C-CALL-1
      response = C.process_request(request)
    • C-CALL-2 response is None,
      response = D(_get_response)(request)
      D-CALL-1 response =
      D.process_request(request)
    • D-CALL-2 response is None,
      response = _get_response(request)
    • D-CALL-3 response is not None
      response = D.process_response(request, response)
      retrun C-CALL-2
  • C- CALL-3 response is not None,
    response = C.process_response(request, response)
    retrun B-CALL-2
  • B-CALL-3 response is not None,
    response = B.process_response(request, response)
    retrun A-CALL-2
  • A-CALL-3 response is not None,
    response = A.process_response(request, response)
    retrun response
  • MiddlewareMixin的

    __call__
    方法中分为三步:

    1. response = self.process_request(request)
    2. response = self.get_response(request)
    3. response = self.process_response(request, response)

    最后

    return response

    解释下这个调用层级图:

    A-CALL-1: 代表执行 中间件A实例对象的

    __call__
    的第一步

    A-CALL-2: 代表执行 中间件A实例对象的

    __call__
    的第二步

    A-CALL-3: 代表执行 中间件A实例对象的

    __call__
    的第三步

    最终

    _get_response
    函数在被调用前依次会调用
    A
    ,
    B
    ,
    C
    ,
    D
    process_request
    方法,然后再执行
    _get_response
    函数,最后再依次调用它们的
    process_response
    方法。

    在D-CALL-2 response is None,

    response = _get_response(request)
    这一步中,会调用
    _get_response
    方法,我们再来看一下这个方法:

    class BaseHandler:
    
    def _get_response(self, request):
    # 调用视图函数和process_view函数, process_exception函数,process_template_response函数
    
    response = None
    ...
    # 1. 找通过url匹配找到视图函数callback
    resolver_match = resolver.resolve(request.path_info)
    callback, callback_args, callback_kwargs = resolver_match
    ...
    
    # 2. 调用_view_middleware列表中的所有中间件的proces_view
    for middleware_method in self._view_middleware:
    response = middleware_method(request, callback, callback_args, callback_kwargs)
    if response:
    break
    
    # 3. 调用视图函数
    if response is None:
    wrapped_callback = self.make_view_atomic(callback)
    try:
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
    except Exception as e:
    response = self.process_exception_by_middleware(e, request)
    ...
    
    # 4. 调用_template_response_middleware列表中保存的中间件的process__template_response方法,如果有异常则会调用_exception_middleware列表中的process_exception方法
    elif hasattr(response, 'render') and callable(response.render):
    for middleware_method in self._template_response_middleware:
    response = middleware_method(request, response)
    # Complain if the template response middleware returned None (a common error).
    if response is None:
    raise ValueError(
    "%s.process_template_response didn't return an "
    "HttpResponse object. It returned None instead."
    % (middleware_method.__self__.__class__.__name__)
    )
    
    try:
    response = response.render()
    except Exception as e:
    response = self.process_exception_by_middleware(e, request)
    
    return response

    这个方法的主要逻辑:

    1. 找通过url匹配找到视图函数

      callback

    2. 调用

      _view_middleware
      列表中的所有中间件的
      proces_view

    3. 调用视图函数

      callback

    4. 调用

      _template_response_middleware
      列表中保存的中间件的
      process__template_response
      方法,如果有异常则会调用
      _exception_middleware
      列表中的
      process_exception
      方法

    总结

    通过继承

    MiddlewareMixin
    类的
    __call__
    方法实现了一种类似递归的调用。

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: