什么是中间件

视图函数适合实现业务功能的纵向切割,我们可以将不同的业务逻辑,分解到不同的视图函数里去实现。

但并不是所有的代码都适合放在视图函数中实现。例如,我们希望通过检测请求对象是否携带了指定的cookie来判断用户是否第一次访问,并将第一次访问的用户重定向到一个新手页面。

显然,在每个视图里都去实现这个功能不是一个好的设计。更好的方案是在请求分发之前,框架提供一个借口以便应用程序注入代码,拦截请求对象并进行必要的处理。

middleware1

在Django框架里,提供了一个叫做中间件的接口实现了这个需求。一个Django中间件就是一个实现了预定接口的类。Django根据注入时间的不同,将中间件分成了不同的类别,分别实现不同的接口方法。

对于上面的示例需求,我们可以定义一个请求处理的中间件,它需要实现一个process_request()函数,Django会在向视图分发之前调用此函数:

class FreshmanMiddleware(object):
    def process_request(self,req):
        if 'vid' in req.COOKIES:
            return None
        else:
            return HttpResponseRedirect('/freshman/')

开启中间件
在定义中间件之后,还要在全局配置中向MIDDLEWARE_CLASS元组里添加这个类的全名。假设我们使用模块文件testmw.py保存了这类:

settings.MIDDLEWARE_CLASSES = ('testmw.FreshmanMiddleware',)

中间件流水线

Django根据注入时间的不同,分为了以下几类
middleware2

  • 请求中间件 - Django为请求匹配路由之前执行中间件的process_request()方法

  • 视图中间件 - Django调用视图函数之前执行中间件的process_view()函数

  • 异常中间件 - 如果调用视图函数时发生了异常,将调用中间件process_exception()方法

  • 模板响应中间件 - 如果之前环节返回的是一个模板响应对象,Django将在实际渲染该模板之前,执行模板响应中间件的process_template_response方法

  • 响应中间件 - Django向WSGI服务器返回结果前,会调用中间件的process_response()方法

这个分类只是概念上的分类,其实一个中间件类是可以实现所有接口方法的,也就属于这几种中间件了。

请求中间件

一个请求中间件应当实现方法process_request(),其原型如下:

process_request(request)

参数request是一个HttpRequest对象。
请求中间件返回None或HttpResponse对象。如果返回的是HttpResponse对象,Django将跳过视图中间件处理、匹配的视图处理环节。如果返回的是None,Django将正常执行其他请求中间件以及后续的环节:
mw-request
如果有多个请求中间件,Django会按照它们在MIDDLEWARE_CLASSES中注册的顺序依次执行。如上图,我们定义了三个请求中间件A、B和C,当一个请求进来时,Django先执行A,如果A返回None,将继续执行B,如果B也返回None,执行C。

视图中间件

一个视图中间件应当实现方法process_view(),其原型如下:
process_view(request,view_func,view_args,view_kwargs)
在Django调用视图中间件之前,已经找到了匹配的视图函数并提取了必要的参数, 因此,process_view()方法的后三个参数,分别代表了视图函数、位置参数列表 和关键字参数字典。
和请求中间件一样,视图中间件也有两个返回值None和HttpResponse。如果返回的是HttpResponse对象,Django将跳过其他的视图中间件和后续视图函数处理环节:
mw-view
和请求中间件一样,多个视图中间件出现时,Django会根据MIDDLEWARE_CLASSES中得顺序执行。如上图所示,有三个视图中间件A、B和C,当请求进入视图中间件处理环节,如果A返回None,将继续调用B,如果B也返回None,C将被调用。

异常中间件

一个异常中间件应当实现process_exception()方法,其原型如下:

process_exception(request,exception)

同样,异常中间件应当返回None或者HttpResponse对象。如果返回的是一个HttpResponse对象,那么将继续后续的模板响应中间件、响应中间件;如果返回的是None,Django将跳过后续的环节,直接调用默认的异常处理。
mw-except
如果有多个异常中间件,那么Django将按照它们在配置对象的MIDDLEWARE_CLASSES 属性中注册的逆序执行。例如上图中,我们定义了三个异常中间件A、B和C。那么,当视图函数出现异常时,Django将先执行C,如果C返回None,将执行B,如果B也返回None,将继续执行A。

响应中间件

一个响应中间件应当实现process_response()方法,其原型如下:

process_response(request,response)

响应中间件必须返回一个HttpResponse对象。

响应中间件

如果有多个响应中间件,那么Django将按照它们在配置对象的MIDDLEWARE_CLASSES 属性中注册的逆序执行。上图描述很清晰,不再赘述。