2022-06-19
devops
00
请注意,本文编写于 350 天前,最后修改于 350 天前,其中某些信息可能已经过时。

目录

django中间件
django的洋葱模型(django中间件链)
其他应用的链式中间件
理解中间件
Hooks and application order¶
测试中间件原理
准备后期环境
注册中间件
验证中间件加载顺序
中间件处理顺序
中间件处理process_request请求时,不合格直接响应
中间件processrequest处理请求均完成时,处理processview时,直接响应
所有的processrequest和所有的processview均返回none
在process_response中自定义header
作用
内建中间件

django中间件

django中间件类似其他框架的拦截器 中间件过滤所有请求和响应

python
# handler class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() # 加载中间件 def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) request = self.request_class(environ) # 转换请求为字典 response = self.get_response(request) # 获取响应 response._handler_class = self.__class__ status = '%d %s' % (response.status_code, response.reason_phrase) response_headers = [ *response.items(), *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()), ] start_response(status, response_headers) if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'): # If `wsgi.file_wrapper` is used the WSGI server does not call # .close on the response, but on the file wrapper. Patch it to use # response.close instead which takes care of closing all files. response.file_to_stream.close = response.close response = environ['wsgi.file_wrapper'](response.file_to_stream, response.block_size) return response # wsgi.py 入口 application = WSGIHandler() #

django 就是app, app需要可以调用。所以实例可以调用就是__call__方法调用。

而在call之前,已经实例化了,注意上面的实例化会执行, 先self传递到父类实例化然后在当前子类中进行实例化 加载中间件 self.load_middleware()

django的洋葱模型(django中间件链)

django中间件salary/settings.py

python
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', ]

image-20220512165810920

其他应用的链式中间件

来回不一样的中间件处理

image-20220512170026924

理解中间件

https://docs.djangoproject.com/en/3.2/

Middleware: Overview | Built-in middleware classes

def simple_middleware(get_response): # One-time configuration and initialization. def middleware(request): # Code to be executed for each request before # the view (and later middleware) are called. response = get_response(request) # Code to be executed for each request/response after # the view is called. return response return middleware

1.10之后,就有这样的类了, 语法变了。内核不变

class SimpleMiddleware: def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. def __call__(self, request): # Code to be executed for each request before # the view (and later middleware) are called. response = self.get_response(request) # Code to be executed for each request/response after # the view is called. return response

使用1.8文档 https://docs.djangoproject.com/en/1.8/topics/http/middleware/

Hooks and application order

During the request phase, before calling the view, Django applies middleware in the order it’s defined in MIDDLEWARE_CLASSES, top-down. Two hooks are available:

During the response phase, after calling the view, middleware are applied in reverse order, from the bottom up. Three hooks are available:

middleware application order

If you prefer, you can also think of it like an onion: each middleware class is a “layer” that wraps the view.

The behavior of each hook is described below.

注意以上的方法,是 process_request() 在每个中间件上 处理完成之后,再由 process_view() 在每个中间件上处理。

这2个函数,就是在每个中间件上有这2个勾子函数,框架在特定的点上留了一些加载点,如果你挂了一个勾子函数,框架运行时,路过这个中间件,看是否有挂勾子函数,有我就执行一下。

当前版本,只是形式变了, process_request和process_response不存在了,但是内在不变。

回到 https://docs.djangoproject.com/en/3.2/

测试中间件原理

准备后期环境

new出一个包在项目根目录, utils。准备一个文件 utils/middlewares.py

python
class Middleware1: def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. # 1次性生成, load_middleware()时会实例化所有中间件。 print(self.__class__.__name__, ' 初始化') def __call__(self, request): # 处理请求和响应 # Code to be executed for each request before the view (and later middleware) are called. # 解析请求后到视图函数之前 print(self.__class__.__name__, ' 等价于call--process_request勾子 解析请求后到视图函数之前') response = self.get_response(request) # 向洋葱里面钻,直到handler, 再逐层进入 # Code to be executed for each request/response after the view is called. # 视图函数返回响应之后,构建响应前 print(self.__class__.__name__, ' 等价于call--process_response勾子 视图函数返回响应之后,构建响应前') return response def process_view(self, request, view_func, view_args, view_kwargs): print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后,view操作前处理') print(self.__class__.__name__,'process_view[', view_func,']', '视图函数 fn(request, *args, **kwargs)') print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后, view处理之后处理') class Middleware2: def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. # 1次性生成, load_middleware()时会实例化所有中间件。 print(self.__class__.__name__, ' 初始化') def __call__(self, request): # 处理请求和响应 # Code to be executed for each request before the view (and later middleware) are called. # 解析请求后到视图函数之前 print(self.__class__.__name__, ' 等价于call--process_request勾子 解析请求后到视图函数之前') response = self.get_response(request) # 向洋葱里面钻,直到handler, 再逐层进入 # Code to be executed for each request/response after the view is called. # 视图函数返回响应之后,构建响应前 print(self.__class__.__name__, ' 等价于call--process_response勾子 视图函数返回响应之后,构建响应前') return response def process_view(self, request, view_func, view_args, view_kwargs): print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后,view操作前处理') print(self.__class__.__name__,'process_view[', view_func,']','视图函数 fn(request, *args, **kwargs)') print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后, view处理之后处理')

注册中间件

copy middleware中 copy reference

python
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', 'utils.middlewares.Middleware1', 'utils.middlewares.Middleware2', ]

验证中间件加载顺序

# 重启服务 Django version 3.2.8, using settings 'salary.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CTRL-BREAK. Middleware2 初始化 Middleware1 初始化

返向初始化,load_middeware有一个revert()

实际上中间件 完成之后由处理视图. 包装过程就是 m2先包装handler, 再m1. 所以请求来了,先m1处理,m2处理。响应后,先m2处理,后m1处理。 参考 5.1的洋葱模型

从上到下,就是洋葱模型的从外到内 先初始化从内到外(列表从下到上) 请求由外到内(列表从上到下)。响应从内到外(列表从下到上)

请求test函数的get方法 http://127.0.0.1:8000/emps/

python
Middleware2 初始化 Middleware1 初始化 [13/May/2022 15:51:25] "GET /emps/ HTTP/1.1" 200 33 Middleware1 等价于call--process_request勾子 解析请求后到视图函数之前 # M1 进入 response = self.get_response(request) Middleware2 等价于call--process_request勾子 解析请求后到视图函数之前 # M2 进入 response = self.get_response(request) Middleware1 等价于call--process_view勾子 处理完 process_request之后,view操作前处理 # 进入view Middleware1 process_view[ <function test at 0x000001644E6F7F70> ] 视图函数 fn(request, *args, **kwargs) Middleware1 等价于call--process_view勾子 处理完 process_request之后, view处理之后处理 Middleware2 等价于call--process_view勾子 处理完 process_request之后,view操作前处理 # 进入view Middleware2 process_view[ <function test at 0x000001644E6F7F70> ] 视图函数 fn(request, *args, **kwargs) Middleware2 等价于call--process_view勾子 处理完 process_request之后, view处理之后处理 # 进入到handler <WSGIRequest: GET '/emps/'> {} # 响应 Middleware2 等价于call--process_response勾子 视图函数返回响应之后,构建响应前 # 响应 M2 Middleware1 等价于call--process_response勾子 视图函数返回响应之后,构建响应前 # 响应 M1

image-20220513155512790

中间件处理顺序

中间件处理process_request请求时,不合格直接响应

diff
+from django.http import JsonResponse class Middleware1: def __call__(self, request): # 处理请求和响应 # Code to be executed for each request before the view (and later middleware) are called. # 解析请求后到视图函数之前 print(self.__class__.__name__, ' 等价于call--process_request勾子 解析请求后到视图函数之前') + return JsonResponse({'a': 1, 'b': [1,3,5]}) response = self.get_response(request) # 向洋葱里面钻,直到handler, 再逐层进入

这样,在middleware处理请求,就不会向内钻了。 请求 http://127.0.0.1:8000/emps/ 无论什么方法

[13/May/2022 16:07:16] "PUT /emps/ts2 HTTP/1.1" 200 24 Middleware1 等价于call--process_request勾子 解析请求后到视图函数之前

这样无论什么路由, 什么方法, 直接就反弹回客户端。

M1不加return, M2加return

diff
+from django.http import JsonResponse class Middleware2: def __call__(self, request): # 处理请求和响应 # Code to be executed for each request before the view (and later middleware) are called. # 解析请求后到视图函数之前 print(self.__class__.__name__, ' 等价于call--process_request勾子 解析请求后到视图函数之前') + return JsonResponse({'a': 1, 'b': [1,3,5]}) response = self.get_response(request) # 向洋葱里面钻,直到handler, 再逐层进入

请求 http://127.0.0.1:8000/emps/ 无论什么方法

Middleware2 初始化 Middleware1 初始化 [13/May/2022 16:03:55] "PUT /emps/ts2 HTTP/1.1" 200 24 Middleware1 等价于call--process_request勾子 解析请求后到视图函数之前 # M1 request处理 Middleware2 等价于call--process_request勾子 解析请求后到视图函数之前 # M2 request处理,直接返回,跳过process_view, view function. 直接到自己前一个中间件的response Middleware1 等价于call--process_response勾子 视图函数返回响应之后,构建响应前 # M1就是M2前一个中间件的response

说明M2返回时,会跳到M1 response = self.get_response(request) 之后

image-20220513161137384

return JsonResponse({'a': 1, 'b': [1,3,5]}) response = self.get_response(request) # 向洋葱里面钻,直到handler, 再逐层进入

注意response = self.get_response(request) 之前提前return时

中间件process_request处理请求均完成时,处理process_view时,直接响应

process_view均处理完,return None 均返回None, 最终可以到达 view函数。

如果process_view提前返回时,将从最后一个中间件开始处理响应

diff
from django.http import JsonResponse class Middleware1: def process_view(self, request, view_func, view_args, view_kwargs): print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后,view操作前处理') print(self.__class__.__name__,'process_view[', view_func,']', '视图函数 fn(request, *args, **kwargs)') print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后, view处理之后处理') # return None # 默认行为 下一个process_view + return JsonResponse({'test': 'm1.processview'})

现在请求 http://127.0.0.1:8000/emps/ 无论什么方法

python
# 初始化 Middleware2 初始化 Middleware1 初始化 # request处理完 Middleware1 等价于call--process_request勾子 解析请求后到视图函数之前 # M1 Middleware2 等价于call--process_request勾子 解析请求后到视图函数之前 # M2 # 处理M1的view提前return 非None Middleware1 等价于call--process_view勾子 处理完 process_request之后,view操作前处理 Middleware1 process_view[ <function test at 0x0000017B6E8A7F70> ] 视图函数 fn(request, *args, **kwargs) Middleware1 等价于call--process_view勾子 处理完 process_request之后, view处理之后处理 # 直接跳过view函数,来到响应 Middleware2 等价于call--process_response勾子 视图函数返回响应之后,构建响应前 Middleware1 等价于call--process_response勾子 视图函数返回响应之后,构建响应前

image-20220513163050903

所有的process_request和所有的process_view均返回none

process_request走完之后,可以通过路由,可以知道谁是view

image-20220513155512790

在process_response中自定义header

utils/middlewares.py

diff
from django.http import JsonResponse,HttpResponse class Middleware1: def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. # 1次性生成, load_middleware()时会实例化所有中间件。 print(self.__class__.__name__, ' 初始化') def __call__(self, request): # 处理请求和响应 # Code to be executed for each request before the view (and later middleware) are called. # 解析请求后到视图函数之前 print(self.__class__.__name__, ' 等价于call--process_request勾子 解析请求后到视图函数之前') response:HttpResponse = self.get_response(request) # 向洋葱里面钻,直到handler, 再逐层进入 # Code to be executed for each request/response after the view is called. # 视图函数返回响应之后,构建响应前 print(self.__class__.__name__, ' 等价于call--process_response勾子 视图函数返回响应之后,构建响应前') + response.headers['X-MY'] = 'songliangcheng' return response def process_view(self, request, view_func, view_args, view_kwargs): print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后,view操作前处理') print(self.__class__.__name__,'process_view[', view_func,']', '视图函数 fn(request, *args, **kwargs)') print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后, view处理之后处理') # return None # 默认行为 下一个process_view return None class Middleware2: def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. # 1次性生成, load_middleware()时会实例化所有中间件。 print(self.__class__.__name__, ' 初始化') def __call__(self, request): # 处理请求和响应 # Code to be executed for each request before the view (and later middleware) are called. # 解析请求后到视图函数之前 print(self.__class__.__name__, ' 等价于call--process_request勾子 解析请求后到视图函数之前') response = self.get_response(request) # 向洋葱里面钻,直到handler, 再逐层进入 # Code to be executed for each request/response after the view is called. # 视图函数返回响应之后,构建响应前 print(self.__class__.__name__, ' 等价于call--process_response勾子 视图函数返回响应之后,构建响应前') return response def process_view(self, request, view_func, view_args, view_kwargs): print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后,view操作前处理') print(self.__class__.__name__,'process_view[', view_func,']', '视图函数 fn(request, *args, **kwargs)') print(self.__class__.__name__, ' 等价于call--process_view勾子 处理完 process_request之后, view处理之后处理') return None

http://127.0.0.1:8000/emps/

响应头就有自己的东西

作用

settings.py定义中间件列表,初始化反向初始化,包粽子从里到外,访问里从外到里,就是列表正向访问。

先处理 从外到内 process_request, 再处理 从外到内 process_view, 再处理view, 最后处理 从内到外 process_response.

process_request:

  • IP,参数,检查请求。

  • ip分析 过滤统计,useragent分析 过滤统计

  • 合格通过,不合格, 直接响应。

process_response:

  • header处理,字段替换
  • 异常响应统一处理 404处理,400系列处理,500系列处理。

内建中间件

python
'django.contrib.sessions.middleware.SessionMiddleware', # 从request.COOKIE中获取sessionid, 查数据库表,获取此sid对应的信息。#request.session=创建Session类的实例。 'django.contrib.auth.middleware.AuthenticationMiddleware', # 通过session中间件提供的sessionid, 获取到userid, 通过uid查表获取user, 否则就是匿名用户。 #request.user=要么是user实例或匿名用户实例。

在pycharm中,选中 django.contrib.sessions.middleware.SessionMiddleware 按2下shift 就会搜索字符,查找到内建的类

request到达视图函数时,已经经过上面两个中间件,所以request上一定有这2个属性session, user

本文作者:mykernel

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!