中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每一个中间件组件都负责作一些特定的功能。python
可是因为其影响的是全局,因此须要谨慎使用,使用不当会影响性能。django
中间件是帮助咱们在视图函数执行以前和执行以后均可以作一些额外的操做,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。app
中间件中主要能够定义下面5个钩子函数来对请求的输入输出作处理:框架
它们的主要做用参见官方文档.函数
这5个钩子函数的触发时机能够见下面的图.源码分析
说明: 上面说的顺序都是中间件在settings文件中列表的注册顺序.性能
我对中间件如何会作出上面的处理顺序, 比较好奇, 因而就去研究了下Django的源码.学习
首先. Django在启动初始化一系列环境配置, 包括wsgi协议的实现, 也包括中间件组件的初始化. 中间件的初始化入口函数在这.atom
进入到load_middleware
函数, 能够看咱们能够自定义的钩子函数都在这里了, 放在5个列表里面. 接下来判断settings里面的MIDDLEWARE
配置项是否为空, 为空的话会去django.conf.global_settings.py
里面的默认的配置文件加载中间件.默认的中间件只有下面两个.url
# django.conf.global_settings.py MIDDLEWARE_CLASSES = [ 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', ] MIDDLEWARE = None
通常咱们都不会注释掉项目下的7个默认中间件, 因此上面的代码会走else
分支, 这里的else
分支是初始化中间件组件的核心逻辑.最后会把全部的中间件放到self._middleware_chain
这个中间件处理链之中. 这里的设计思想我也是花了好一段时间才想明白.
接下来展开else
代码块, 到了核心部分. 下面列出的else里面的源码部分.
else: # 这里是将handler赋初始值为self._get_response, 这个函数是用来匹配请求url与调用视图函数 # 并应用view, exception, template_response中间件. handler = convert_exception_to_response(self._get_response) # 接下来一段代码比较难理解, 但确是设计的精髓. # 首先, 遍历咱们配置的中间件列表, 只不过这里是逆序遍历, 至于为何, 往下看就知道了 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 six.text_type(exc): logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc) else: logger.debug('MiddlewareNotUsed: %r', middleware_path) continue # 实例化对象为None, 由于中间件还能够是函数形式 if mw_instance is None: raise ImproperlyConfigured( 'Middleware factory %s returned None.' % middleware_path ) # 将process_view方法添加到_view_middleware列表的开头 if hasattr(mw_instance, 'process_view'): self._view_middleware.insert(0, mw_instance.process_view) # 将process_template_response方法添加到_template_response_middleware列表的末尾 # 这里也能解释为何处理模板以及下面的异常时逆序(按照注册顺序逆序)处理的 if hasattr(mw_instance, 'process_template_response'): self._template_response_middleware.append(mw_instance.process_template_response) if hasattr(mw_instance, 'process_exception'): self._exception_middleware.append(mw_instance.process_exception) # 将当前中间件实例化对象从新绑定给handler变量 handler = convert_exception_to_response(mw_instance) # 最后这个handler指向的是中间件列表的第一个实例对象 self._middleware_chain = handler
这样看完以后上面的分析以后应该仍是难以理解思路, 这须要看这个中间件实例化对象的定义形式, 看下面这个中间件类定义部分;
class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__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
每一个中间件类都有两个基本方法, 初始化时会保存下一个get_response对象, 最后调用中间件实例化对象就可以不停的调用存储的get_response对象, 这个是可以实现链式调用的关键. 上面的思想看下图演示.
这时候再来看上面的代码, 起始的handler首先指向最里层的get_response方法, 而后从列表最后的中间件开始遍历, 把handler(此时是get_response)当作参数, 生成一个中间件对象CommonMiddleware, 此时handler指向了这个新的对象, 而后依次循环, 重复上面的操做, 至关于一层包着一层.
最后handler会指向最外层的中间件对象. 而后赋值给self._middleware_chain
这个变量.
当咱们调用self._middleware_chain(request)
方法的时候, 就会触发这个中间件的__call__
方法. 这个时候从最外层中间件进行, 执行process_request
方法, 只要不产生response, 就会一直调用内层的中间件变量, 触发__call__
方法, 一直到最里层, 开始处理视图相关的功能. 在url匹配以后, 调用视图函数以前, 会遍历全部中间件的process_view方法. 若是返回的结果为None, 则去调用咱们书写的视图函数, 若是触发异常, 则会遍历处理全部process_exception方法, 若是没有则去调用符合条件的process_template_response方法. 触发异常一样会触发process_exception方法. 最后会把结果返回回去. 而这时候会从最里层一层层往外返回. 这就可以解释中间件钩子函数的触发顺序.
这里再放一个最里层的处理逻辑, 有一些删减
# django/core/handlers/base.py def _get_response(self, request): response = None # 路由匹配 if hasattr(request, 'urlconf'): urlconf = request.urlconf set_urlconf(urlconf) resolver = get_resolver(urlconf) else: resolver = get_resolver() resolver_match = resolver.resolve(request.path_info) # 这个callback就是咱们的视图函数, 后两个是视图函数可能须要的参数 callback, callback_args, callback_kwargs = resolver_match request.resolver_match = resolver_match # 应用 view middleware 中间件 for middleware_method in self._view_middleware: response = middleware_method(request, callback, callback_args, callback_kwargs) # 只要有response返回, 就马上中止遍历 if response: break if response is None: # 给视图函数包装一层 wrapped_callback = self.make_view_atomic(callback) try: # 这里是调用视图函数 response = wrapped_callback(request, *callback_args, **callback_kwargs) except Exception as e: # 有异常就进入exception view处理 response = self.process_exception_by_middleware(e, request) # 这个不经常使用的process_template_response功能, 看源码能够清楚的知道为何 # 返回的结果为啥须要有render方法了 elif hasattr(response, 'render') and callable(response.render): for middleware_method in self._template_response_middleware: response = middleware_method(request, response) # ... try: response = response.render() except Exception as e: response = self.process_exception_by_middleware(e, request) return response
有了上面的部分源码分析, 最后能够明白中间件为何会是以这样的顺序处理请求和响应的. Django这种设计思想是我重来没接触过的, 学习完以后感触很是深, 只能感慨别人程序的设计之精妙, 本身还要好好学习.