二、中间件(middleware)
1. 中间件(middleware)
Django中的中间件主要实现一些附加功能,在request被用户handler处理前,以及用户handler处理后生存的response进行处理。因此大部分中间件的主要功能是实现了process_request或者process_response之一,当然也可以两者都实现,意味着该中间件涉及到处理的request和response的处理。
1.1 类视图
在中间件的类关系中,基本类是MiddlewareMixin,其他所有的中间件类都继承自该类,该类仅有一个成员变量get_response,该变量在连接各个中间件起着非常重要的作用。该类有两个成员函数:__init__和__call__,前者用于实现初始化,后者执行主要的功能。
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
其中process_request和process_response自不必多言,中间件需要实现的主要功能,通过hasattr()调用,判断当前中间件是否具有process_request和process_response,有则调用。
不同功能的中间件,根据要完成的功能,可以扩展自己的成员变量和成员函数,通常,自定义的成员函数被process_request和process_response调用,即这些自定义的成员函数通常不对外开放。
其中,__init__成员函数有入参函数之一: get_response,这在后续的加载中间件时很重要好。
下图是中间件SecurityMiddleware类的类视图关系图,其他中间件的视图关系类似。
1.2 中间件初始化
在工程的配置文件(Project/Project/setting.py)中指定要使用的中间件,注意各插件在序列中的顺序,这决定了当请求到来时,各个中间件的执行顺序,排在最前面的中间件得到最先执行,最后的中间件最后执行。其表现通常如下所示:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
下面来看,中间件是如何被加载和初始化的。class BaseHandler(object):中定义了load_middleware接口,具体细节如下:
handler = convert_exception_to_response(self._get_response) /*handler[0]*/
for middleware_path in reversed(settings.MIDDLEWARE):
middleware =
import_string(middleware_path)
mw_instance = middleware(handler)
handler = convert_exception_to_response(mw_instance) /*handler[i]*/
self._middleware_chain = handler /*handler[end]*/
在上述例程中,开始遍历所有中间件之前,handler(即handler[0])赋值给调用实体的_get_response成员函数。 (比如class WSGIHandler(base.BaseHandler),WSGIHandler. load_middleware()调用之后,handler[0] = WSGIHandler._get_response = BaseHandler._get_response())。遍历所有中间件过程中,加载并初始化(middleware()调用对应着__init__成员函数)中间件,因此中间件的get_response赋值为handler[i],注意middleware()返回一个中间件类实体,接着调用调用handler = convert_exception_to_response (mw_instance),convert_exception_to_response只是对输入函数进行了容错封装,在分析逻辑时,可以简单看成输入函数本身,因此,可以看成handler = mw_instance(request),对于一个类实体调用即调用__call__成员函数,从目前来看,大部分的中间件并没有重载该成员函数,因此是直接调用基类的成员函数,即: MiddlewareMixin. __call__(self)。因此handler[i] = MiddlewareMixin. __call__(self)。注意该函数有一个self输入参数,各个中间件在调用时,传入该中间件对应的self实体,这样self.process_request, self.process_response的调用就分别对应着不同中间夹的处理函数。load_middleware()调用之后,各个中间件通过get_response链接起来,这样一个request请求到来时,可以顺序通过各个中间件依次进行处理,如下图所示:
在调用的最后,将self._middleware_chain = handler[end],类似于将中间件链表的链表头保存起来,下次处理时,从该链表头开始进行遍历处理。
1.3 中间件使用
各个中间件的调用顺序如下递归图所示:
在类视图一节中对__call__基类成员函数进行过介绍,该函数主要有先后执行的三个函数构成:process_request()、get_response()、process_response(),而get_response()通常指向下一个中间件的__call__成员函数(中间件链表的最后一个中间件除外,其get_response()指向handler[0]),由于各个中间件的get_response()这一特性(绝大部分指向基类__call__成员函数),使得中间件链表处理有点类似递归调用的感觉。A,B,A1,B1,A2,B2,C2,C1,C,这一递归调用顺序,决定了各个中间件处理的一些特点:
1、最先进行流程处理的中间件,其process_request()(如果存在的话)最先被执行,但是其process_response()(如果存在的话)却最后得到处理。
2、最后进行流程处理的中间件,其process_request()(如果存在的话)最后被执行,但是其process_response()(如果存在的话)紧接着得到处理。
3、在中间件处理流程中,如果出现某个中间件的process_request()返回了response,这种情况通常是处理过程中出现了异常情况,该中间件后续的中间件不再参与处理,直接调用该中间件的process_response()(如果存在的话),或者直接返回。