按照settings.MIDDLEWARE的顺序,先由上至下执行所有的process_request,然后由上至下执行所有的process_view,最后由下至上执行所有的process_response。如:

  1. MIDDLEWARE = [
  2. "django.middleware.security.SecurityMiddleware",
  3. "django.middleware.common.CommonMiddleware",
  4. "django.middleware.clickjacking.XFrameOptionsMiddleware",
  5. "apps.api.middleware_mgr.Middleware",
  6. ]

settings.MIDDLEWARE存在的情况下,Django加载中间件的源码:

  1. # django/core/handlers/base.py
  2. class BaseHandler(object):
  3. def __init__(self):
  4. self._request_middleware = None
  5. self._view_middleware = None
  6. self._template_response_middleware = None
  7. self._response_middleware = None
  8. self._exception_middleware = None
  9. self._middleware_chain = None
  10. def load_middleware(self):
  11. # _get_response是一个方法,它的功能是按顺序调用中间件的process_view方法。并且在最核心执行被层层包裹的实际视图
  12. # This method is everything that happens inside the request/response middleware.
  13. # convert_exception_to_response是一个装饰器,相当于是为_get_response加了一个在出错后仍然可以返回HttpResponse的功能
  14. handler = convert_exception_to_response(self._get_response)
  15. # 逆序访问 settings.MIDDLEWARE 中定义的中间件路径
  16. for middleware_path in reversed(settings.MIDDLEWARE):
  17. # 这个middleware是settings.MIDDLEWARE中定义的中间件类,如 <class \'apps.auth2.middleware_mgr.AdMiddleware\'>
  18. middleware = import_string(middleware_path)
  19. # mw_instance是中间件类的实例。
  20. # 由于大多数中间件都继承自MiddlewareMixin,因此初始化中间件实例的时候需要提供一个get_response方法
  21. # 而handler刚好就是我们需要的这个方法
  22. # (见下文MiddlewareMixin的__init__方法)
  23. mw_instance = middleware(handler)
  24. if hasattr(mw_instance, \'process_view\'):
  25. # self._view_middleware 是按照settings.MIDDLEWARE的顺序,由上至下生成的
  26. self._view_middleware.insert(0, mw_instance.process_view)
  27. if hasattr(mw_instance, \'process_template_response\'):
  28. # self._template_response_middleware 是按照settings.MIDDLEWARE的顺序,由下至上生成的
  29. self._template_response_middleware.append(mw_instance.process_template_response)
  30. if hasattr(mw_instance, \'process_exception\'):
  31. # self._exception_middleware 是按照settings.MIDDLEWARE的顺序,由下至上生成的
  32. self._exception_middleware.append(mw_instance.process_exception)
  33. # 思考:为什么不加载process_request和process_response?
  34. # 一定要理解这个地方,其实middleware就像套娃的一层一样
  35. # 它的实例初始化需要接受一个get_response方法,这个方法就相当于这层套娃的内部
  36. # 然后,中间件的实例可以被当做函数调用,即执行中间件实例的__call__方法
  37. # 那么这个中间件实例可以被视为另一个get_response方法,被外层的中间件使用
  38. # 这个handler,就是将当前层中间件实例套上之后的套娃
  39. handler = convert_exception_to_response(mw_instance)
  40. # 只有当所有中间件加载完毕后,才会将_middleware_chain赋值为最终的handler(最外层中间件的实例)
  41. # 这个变量被赋值就是初始化完成的标志
  42. self._middleware_chain = handler

整个中间件套娃被执行的入口是BaseHandler的get_response方法

  1. # django/core/handlers/base.py
  2. class BaseHandler(object):
  3. def get_response(self, request):
  4. """Return an HttpResponse object for the given HttpRequest."""
  5. # 根据上文,self._middleware_chain就是最外层中间件的实例,这里调用了最外层中间件的__call__方法
  6. # 然后经过层层中间件抵达核心执行view,再层层递出response,这个response就是最终的结果
  7. response = self._middleware_chain(request)
  8. # This block is only needed for legacy MIDDLEWARE_CLASSES
  9. # if MIDDLEWARE is used, self._response_middleware will be empty.
  10. # 这里回答了load_middleware为什么不加载process_response的问题,self._response_middleware只会在这里用到
  11. # 如果定义了MIDDLEWARE,self._response_middleware将会为空
  12. try:
  13. # Apply response middleware, regardless of the response
  14. for middleware_method in self._response_middleware:
  15. ...
  16. return response

看看最外层中间件的__call__方法,中间件如果继承自MiddlewareMixin,则会执行父类的__call__方法

  1. class MiddlewareMixin(object):
  2. def __init__(self, get_response=None):
  3. self.get_response = get_response
  4. super(MiddlewareMixin, self).__init__()
  5. def __call__(self, request):
  6. response = None
  7. if hasattr(self, \'process_request\'):
  8. # 这里执行的是子类(最外层中间件)的process_request方法,即settings.MIDDLEWARE的第一个列出的中间件
  9. # 因此process_request是按由上到下的顺序执行的,并且在load_middleware不需要被加载
  10. # 它的执行顺序由套娃的结构维护
  11. response = self.process_request(request)
  12. # process_request发生错误后将返回一个HttpResponse对象,下层中间件将不会被执行
  13. if not response:
  14. # 没有发生错误,继续执行下层中间件。这个get_response方法实际上是下层中间件实例的__call__方法
  15. response = self.get_response(request)
  16. # response经过下层的一系列中间件返回,执行当前层的process_response,并返回响应
  17. # 最外层的process_response会最后执行
  18. # 因此process_response是按settings.MIDDLEWARE的顺序由下至上执行的,并且在load_middleware不需要被加载
  19. # 它的执行顺序由套娃的结构维护
  20. if hasattr(self, \'process_response\'):
  21. response = self.process_response(request, response)
  22. return response

中间件的最核心是_get_response方法

  1. def _get_response(self, request):
  2. """
  3. Resolve and call the view, then apply view, exception, and
  4. template_response middleware. This method is everything that happens
  5. inside the request/response middleware.
  6. """
  7. # 在这里按照settings.MIDDLEWARE的顺序依次执行中间件的process_view
  8. for middleware_method in self._view_middleware:
  9. # process_view处理失败或者出于一些其他的原因需要中止请求,就返回一个HttpResponse
  10. # 正常情况下,应返回None
  11. response = middleware_method(request, callback, callback_args, callback_kwargs)
  12. if response:
  13. break
  14. if response is None:
  15. # 执行实际的视图
  16. wrapped_callback = self.make_view_atomic(callback)
  17. response = wrapped_callback(request, *callback_args, **callback_kwargs)
  18. # If the response supports deferred rendering, apply template
  19. # response middleware and then render the response
  20. # 如果有渲染的需求才会使用process_template_response方法
  21. elif hasattr(response, \'render\') and callable(response.render):
  22. for middleware_method in self._template_response_middleware:
  23. response = middleware_method(request, response)
  24. ...
  25. return response
表头 表头 表头 表头
process_request 请求刚到来,执行视图之前 配置列表的正序 None或者HttpResponse对象
process_response 视图执行完毕,返回响应时 逆序 HttpResponse对象
process_view process_request之后,路由转发到视图,执行视图之前 正序 None或者HttpResponse对象
process_exception 视图执行中发生异常时 逆序 None或者HttpResponse对象
process_template_response 视图刚执行完毕,process_response之前 逆序 实现了render方法的响应对象

注意middleware方法的输入参数都是固定的

  1. # utils/my_middleware.py
  2. from django.utils.deprecation import MiddlewareMixin
  3. class MyMiddleware1(MiddlewareMixin):
  4. def __str__(self):
  5. return "MyMiddleware1"
  6. def process_request(self, request):
  7. print(self, " processing request...")
  8. def process_view(self, request, view_func, view_args, view_kwargs):
  9. print(self, " processing view...")
  10. def process_response(self, request, response):
  11. print(self, " processing response...")
  12. return response
  13. def process_exception(self, request, exception):
  14. print(self, " processing exception...")
  15. class MyMiddleware2(MiddlewareMixin):
  16. def __str__(self):
  17. return "MyMiddleware2"
  18. def process_request(self, request):
  19. print(self, " processing request...")
  20. def process_view(self, request, view_func, view_args, view_kwargs):
  21. print(self, " processing view...")
  22. def process_response(self, request, response):
  23. print(self, " processing response...")
  24. return response
  25. def process_exception(self, request, exception):
  26. print(self, " processing exception...")
  1. # settings.py
  2. MIDDLEWARE = [
  3. \'django.middleware.security.SecurityMiddleware\',
  4. \'django.contrib.sessions.middleware.SessionMiddleware\',
  5. \'django.middleware.common.CommonMiddleware\',
  6. \'django.middleware.csrf.CsrfViewMiddleware\',
  7. \'django.contrib.auth.middleware.AuthenticationMiddleware\',
  8. \'django.contrib.messages.middleware.MessageMiddleware\',
  9. \'django.middleware.clickjacking.XFrameOptionsMiddleware\',
  10. \'utils.my_middleware.MyMiddleware1\',
  11. \'utils.my_middleware.MyMiddleware2\'
  12. ]
  1. # urls.py
  2. from django.conf.urls import url
  3. from demo.views import mid_test
  4. urlpatterns = [
  5. url(r"^midtest/", mid_test),
  6. ]
  1. # views.py
  2. from django.http import HttpResponse
  3. def mid_test(request):
  4. print(\'in mid_test view\')
  5. return HttpResponse(\'ok\')

访问 http://127.0.0.1:8000/midtest/ ,可在控制台得到这样的输出,验证了之前关于中间件执行顺序的理论

  1. MyMiddleware1 processing request...
  2. MyMiddleware2 processing request...
  3. MyMiddleware1 processing view...
  4. MyMiddleware2 processing view...
  5. in mid_test view
  6. MyMiddleware2 processing response...
  7. MyMiddleware1 processing response...
  8. [08/Jun/2020 17:32:54] "GET /midtest/ HTTP/1.1" 200 2

如果process_request返回了一个HttpResponse对象

  1. class MyMiddleware1(MiddlewareMixin):
  2. def __str__(self):
  3. return "MyMiddleware1"
  4. def process_request(self, request):
  5. print(self, " processing request...")
  6. return HttpResponse("break")

下层中间件将不会被执行,response会按原路返回

  1. MyMiddleware1 processing request...
  2. MyMiddleware1 processing response...
  3. [08/Jun/2020 18:08:21] "GET /midtest/ HTTP/1.1" 200 5

如果process_view返回了一个HttpResponse对象,同样

  1. class MyMiddleware1(MiddlewareMixin):
  2. def __str__(self):
  3. return "MyMiddleware1"
  4. def process_request(self, request):
  5. print(self, " processing request...")
  6. return HttpResponse("break")
  1. MyMiddleware1 processing request...
  2. MyMiddleware2 processing request...
  3. MyMiddleware1 processing view...
  4. MyMiddleware2 processing response...
  5. MyMiddleware1 processing response...
  6. [08/Jun/2020 18:11:00] "GET /midtest/ HTTP/1.1" 200 5

如果视图引发了异常,如

  1. from django.http import HttpResponse
  2. def mid_test(request):
  3. print(\'in mid_test view\')
  4. 1/0
  5. return HttpResponse(\'ok\')

则Django会逆序调用中间件的process_exception方法,紧接着逆序调用process_response方法

  1. MyMiddleware1 processing request...
  2. MyMiddleware2 processing request...
  3. MyMiddleware1 processing view...
  4. MyMiddleware2 processing view...
  5. in mid_test view
  6. MyMiddleware2 processing exception...
  7. MyMiddleware1 processing exception...
  8. Internal Server Error: /midtest/
  9. Traceback (most recent call last):
  10. File "/home/youmi/code/V36DemoForPython/venv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
  11. response = get_response(request)
  12. File "/home/youmi/code/V36DemoForPython/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
  13. response = self.process_exception_by_middleware(e, request)
  14. File "/home/youmi/code/V36DemoForPython/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
  15. response = wrapped_callback(request, *callback_args, **callback_kwargs)
  16. File "/home/youmi/code/V36DemoForPython/demo/views.py", line 6, in mid_test
  17. 1/0
  18. ZeroDivisionError: division by zero
  19. MyMiddleware2 processing response...
  20. MyMiddleware1 processing response...

如果process_exception返回了一个HttpResponse对象

  1. class MyMiddleware2(MiddlewareMixin):
  2. def process_exception(self, request, exception):
  3. print(self, " processing exception...")
  4. return HttpResponse(\'not ok\')

页面会显示process_exception返回的内容,而不再使用默认异常处理。并且,此中间件之上的中间件类的process_exception方法不会被调用。但是process_response方法仍然会被逆序调用

  1. MyMiddleware1 processing request...
  2. MyMiddleware2 processing request...
  3. MyMiddleware1 processing view...
  4. MyMiddleware2 processing view...
  5. in mid_test view
  6. MyMiddleware2 processing exception...
  7. MyMiddleware2 processing response...
  8. MyMiddleware1 processing response...

Django中间件 –刘江

Middleware¶

版权声明:本文为luozx207原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/luozx207/p/13141845.html