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_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
之后就实现了一种类似递归的调用。