Bootstrap

Django 中间件的研究

开篇

  • 中间件是什么?

    中间件的本质是一个类实例对象或者一个函数对象,这些对象提供过滤请求、响应、处理异常等功能。重点关键字:过滤
    
    1 中间件可以是类的形式也可以是函数的形式,见官网说明
    
  • 上结论先

    从请求到响应阶段 都可以进行拦截
    第一阶段 处理 process_request 列表 返回None 或者HttpResponse对象
    第二阶段 通过路由映射关系表获取视图函数地址 获得函数(进入视图预处理链process_view) 或者没有(404)--进入响应处理链
    第三阶段 处理 process_view列表(前提是已经有匹配到的视图函数)  process_view 返回None或者HttpReponse
    第四阶段 处理视图函数 生成响应
    第五阶段 处理异常检测 处理异常 返回响应或者None, None 表示交给下一层处理异常
    第六阶段 处理 process_response 列表 不管从哪里发出的响应都要从响应链走,要么从中间走,要么从底层走
    
    中间件类提供过滤请求、响应的钩子,即process_request、process_response等,所有钩子按照中间件类在中间件列表中的顺序组成新的钩子列表,process_response响应钩子、process_exception异常处理钩子是反向执行的
    
  • 上代码(代码所在的文件在项目中的位置不重要,重要的是能找到中间件类的路径)

# 1.11 版本的兼容写法,当然你也可以自己手写一个类,不是很麻烦,我使用继承
# appname/middleware.py
# 每一个类都可以根据需要提供不同的钩子,这些钩子组成钩子列表
from django.utils.deprecation import MiddlewareMixin
class Row1(MiddlewareMixin):
    def process_request(self, request):
        print('row1 请求', request)
        # response = HttpResponse("row1 response")
        # return response

    def process_response(self, request, response):
        print('row1 响应', request, response)
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('row1 view', view_func)

class Row2(MiddlewareMixin):
    def process_request(self, request):
        print('row2 请求', request)

    def process_response(self, request, response):
        print('row2 响应', request, response)
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('row2 view', view_func)

    def process_exception(self, request, exception):
        print('row2 exception', exception)

        return HttpResponse(exception);


class Row3(MiddlewareMixin):
    def process_request(self, request):
        print('row3 请求', request)

    def process_response(self, request, response):
        print('row3 响应', request, response)
        if response.status_code == 404:
            response = HttpResponse('404')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('row3 view', view_func)

    def process_exception(self, request, exception):
        print('row3 exception', exception)
  • 上配置
  • MIDDLEWARE中 中间件类所处的顺序很重要,决定了每个中间件类提供的钩子的执行顺序
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',

    'booktest.middleware.Row1',
    'booktest.middleware.Row2',
    'booktest.middleware.Row3',
]
  • 调用的view函数
# Create your views here.
def index(request):
    # raise Exception('异常')
    return HttpResponse("<b>世界如此美妙<b>")
  • 上运行结果
July 29, 2017 - 05:17:13
Django version 1.11, using settings 'test6.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
生成应用
row1 请求 <WSGIRequest: GET '/'>
row2 请求 <WSGIRequest: GET '/'>
row3 请求 <WSGIRequest: GET '/'>
row1 view <function index at 0x1044e36a8>
row2 view <function index at 0x1044e36a8>
row3 view <function index at 0x1044e36a8>
row3 响应 <WSGIRequest: GET '/'> <HttpResponse status_code=200, "text/html; charset=utf-8">
row2 响应 <WSGIRequest: GET '/'> <HttpResponse status_code=200, "text/html; charset=utf-8">
row1 响应 <WSGIRequest: GET '/'> <HttpResponse status_code=200, "text/html; charset=utf-8">
[29/Jul/2017 05:17:55] "GET / HTTP/1.1" 200 24
  • 其他

    感兴趣的话可以手动抛出异常来看看效果
    
    1 在process_request 阶段, 每个钩子都可以返回None或者HttpResponse对象(自己构造一个),如果返回None,则下一个钩子接着处理,直到URL关系映射表执行匹配操作;如果返回HttpResponse 则从当前中间件层开始反向返回结果,最核心层的view函数不会被执行,当前中间件层的下面中间件类提供的处理响应钩子也不会被执行(1.11版本)
    2 当所有的process_request钩子执行完毕,都返回None,则进入路由关系映射匹配阶段,即根据请求路径找能处理响应的view函数,有两个结果:找到了--->进入process_view钩子列表处理阶段;没找到----->进入process_response钩子列表处理阶段
    3 在process_view 处理阶段可以,每个钩子可以返回None也可以返回HttpResponse 对象,参上
    4 在process_response 阶段,我们可以篡改响应,比如没找到视图函数,我们可以给捏造一个扔给客户
    5 当process_request--->路由关系映射找到视图函数--->process_view 都处理完成了,并且找到了视图函数,中间没有使用钩子构造响应,则view函数开始工作:执行业务逻辑代码,从数据库拉去数据,给模板填坑,构造响应对象
    6 5这个过程可能抛出异常,抛出异常则进入异常钩子列表process_exception,如果所有的异常处理钩子都不能处理异常,程序把异常通过process_response 钩子列表扔给客户
    

    以上 就是整个流程,当然,没有涉及process_template_response钩子列表

  • 上个简图 input/output

  • 每一个中间件都想一张过滤网
    这里写图片描述

  • 上个网络盗图

这里写图片描述
* 上第二个网络图
这里写图片描述

收尾

  • 我们拿这些中间件干什么呢?

    答:玩
    认真的回答:那它做IP拦截,客户端设备识别,过滤恶意请求等等
    
  • 上面的介绍并不全面,还有很多细节没介绍,但不影响对请求/响应整个流程的理解
;