目录
1. 前言
2. 请求的入口
3. 中间件加载的入口
4. 源码中的闭包实现
5. 最后
1. 前言
哈喽,大家好,我是小K,今天咋们分享的内容是:在学会Django中间件之后, 我们继续深入底层源码。
在执行中间件时请求到来总是从前往后逐一匹配,但当响应返回时,执行的中间件顺序往往是反着执行的。
在Django中间件中,采用了多层闭包的形式,以来达到逐一执行中间件,本篇文章主要是从请求的入口开始,简单介绍一下前置。
如果还不会使用中间件的小伙伴可以跳转到这里:
Django之五种中间件定义类型
2. 请求的入口
在同步请求中,请求的入口其实就是 wsgi.py 文件, 不清楚的可以跳转这里,看第2点:
Django源码之路由匹配(下)——图解逐步分析底层源码
3. 中间件加载的入口
在请求到来时,最后返回的是一个WSGIHandler的一个对象
application = get_wsgi_application()
def get_wsgi_application():
django.setup(set_prefix=False)
return WSGIHandler()
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_middleware()
在加载对象时,肯定是少不了执行__init__的初始化方法的
self.load_middleware() # 这其实就是加载中间件的方法
在执行load_middleware()函数时,首先是在自己的类中寻找,如果没有就去父类中寻找
显然,这里的代码是需要我们去父类BaseHandler类中寻找的
这里BaseHandler类和load_middleware的源码:
class BaseHandler:
_view_middleware = None
_template_response_middleware = None
_exception_middleware = None
_middleware_chain = None
def 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 = []
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
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.
adapted_handler = self.adapt_method_mode(
middleware_is_async,
handler,
handler_is_async,
debug=settings.DEBUG,
name="middleware %s" % middleware_path,
)
mw_instance = middleware(adapted_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
else:
handler = adapted_handler
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),
)
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.
self._middleware_chain = handler
ok,我们现在着重来分析load_middleware的源码
4. 源码中的闭包实现
先来看看什么是闭包:
Python奇幻之旅(从入门到入狱基础篇)——附相关资料【中】-****博客
这篇文章里面有一部分介绍了闭包和装饰器
现在来看具体的闭包实现函数:
def convert_exception_to_response(get_response):
if iscoroutinefunction(get_response):
@wraps(get_response)
async def inner(request):
try:
response = await get_response(request)
except Exception as exc:
response = await sync_to_async(
response_for_exception, thread_sensitive=False
)(request, exc)
return response
return inner
else:
@wraps(get_response)
def inner(request):
try:
response = get_response(request)
except Exception as exc:
response = response_for_exception(request, exc)
return response
return inner
最后返回了一个inner函数, 而这个inner调用的是get_response()
最后都被封装进了handler里面
5. 最后
本文只是简单介绍了一下中间件加载的位置,下一篇文章将采用长篇的形式详细讲解中间件是如何通过闭包的形式载入的。今天就先到这里啦!