编辑
2023-07-13
🧰语言-python
00
请注意,本文编写于 707 天前,最后修改于 225 天前,其中某些信息可能已经过时。

目录

1、什么是中间件以及中间件的工作原理
2、Django自带中间件介绍
3、Django的中间件执行顺序
1.自定义中间件

1、什么是中间件以及中间件的工作原理

  中间件事一个镶嵌到django的request/response处理机制中的一个钩子框架。它是一个可以修改django全局输入和输出的一个底层插件系统。

换句话说

HTTP Web服务器工作原理一般都是接受用户发来的请求(request),然后给出响应(resonse)。Django也不例外,其一般工作方式是接受request对象和其他参数,交由视图(view)处理,然后给出它的响应数据:渲染过的html文件或json格式的数据。

然而在实际工作中并不是接收到request对象后,马上交给殊途函数或类处理,也不是在view执行后立马给用户返回response。

事实上,Django最初接受的是HttpRequest对象,而不是request对象,正是中间件的作用把HttpRequest对象和user对象打包成了一个全局变量request对象,这样你才可以view中使用request作为变量或者在模版中随意调用request.user

中间件在整个django的request/response处理机制中的角色如下所示:

HttpRequest —— Middleware  —— View  —— Middleware  ——  HttpResponse

正是由于一个请求HttpRequest在传递给视图view处理前要经过中间件处理,经过View处理后的响应也要经过中间件处理才能返回给用户,我们可以编写自己的中间件实现权限校验,限制用户请求、打印日志、改变输出内容等多种应用场景,比如:

  • 禁止特定IP地址的用户或未登录的用户访问我们的view视图函数
  • 对同一IP地址单位时间内发送的请求数量做出限制
  • 在View视图函数执行前记录用户的IP地址
  • 在View视图函数执行前传递额外的变量或参数
  • 在View视图函数执行前或执行后把特定信息打印到log日志
  • 在View视图函数执行后对response数据进行修改后返回给用户

值得一提的是中间件对django的输入或输出的改变是全局的,反之亦然,如果让你希望对django的输入或输出做出全局性的改变时,需要使用中间件。

举个例子:用@login_required装饰器要求用户必须先登陆才能访问我们的视图函数。试想我们有个网站绝大部分视图函数都需要用户登陆,每个视图函数前面都需要加上@login_required装饰器是比较傻的行为。借助于中间件,我们无需使用装饰器即可全局实现;

只有登陆用户才能访问视图函数,匿名用户跳转到登陆页面。实现原理也很简单:在一个request达到视图函数前,我们先对request.user是否验证通过进行判断,然后再进行跳转。

另外Django对Post表单中携带的CSRF token的全局校验也是通过csrfViewMiddleware这个中间件进行的,而不是通过单个装饰器实现的。

2、Django自带中间件介绍

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', ]
  • SecurityMiddleware:为request/response提供了几种安全改进,无它不安全
  • SessionMiddleware: 开启session会话支持,无它无session
  • CommonMiddleware:基于APPEND_SLASH和PREPEND_WWW的设置来重写URL。如果APPEND_SLASH设为true,并且初始化url没有以斜线结尾以及在URLconf中没找到对应定义,这时形成一个斜线结尾的新的URL;如果PREPEND_WWW设为true,前面缺少www.的url将会被重定向到相同但是以一个www.开头的url。
  • CsrfViewMiddleware:添加跨站点请求伪造的保护,通过向POST表单添加一个隐藏的表单字短,并检查请求中是否有正确的值,无它无Csrf保护
  • AuthenticationMiddleware:在视图函数执行前向每个接受到的user对象添加HttpRequest属性,表示当前登陆的用户。
  • MessageMiddleware:开启基于cookie和会话的消息支持
  • XFrameOptionsMIddleware:对点击劫持的保护。

如果你要实现全站缓存,还需要使用UpdateCacheMiddleware和FetchFromCacheMiddleware,但一定要注意它们的顺序,Update在前和Fetch在后。

MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', ]

除此之外,Django还提供了压缩网站内容的GzipMiddleware,根据用户请求语言返回不同内容的localeMiddleware和给GET请求附加条件的ConditionalGetMiddleware。这些中间件都是可选的。

3、Django的中间件执行顺序

当你在settings.py注册中间件时一定要考虑中间件的执行顺序,中间件在request达到view之前是从上向下执行的,在view执行完成后返回response过程是从下向上执行的。

image.png

1.自定义中间件

自定义中间件你首先要在app所属目录下新建一个文件middleware.py,添加好编写的中间代码,然后在项目settings.py中把它添加到middleware列表进行注册,添加时一定要注意顺序,Django提供了两种编写自定义中间件的形式:函数和类,基本框架如下所示:

  1. 函数实现方式
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. # request请求到达视图函数执行前的代码 response = get_response(request) # Code to be executed for each request/response after # the view is called. 视图函数执行后的代码 return response return middleware
  1. 类实现方式
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

运用到实际案例中。我们编写一个MyFirstMiddleware的中间件,介入到了django的request/response整个处理过程,打印出请求执行过程,并在视图函数前打印出用户是否登陆:

class MyFirstMiddleware: 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. print("接收到request请求,视图函数马上执行") if not request.user.is_authenticated: print("该请求用户尚未登录") response = self.get_response(request) # Code to be executed for each request/response after # the view is called. 视图函数执行后的代码 print("视图函数执行结束,准备提供响应") return response

在settings.py里注册,最后一个中间件是自定义的,app名为users

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', 'users.middleware.MyFirstMiddleware', ]

因为我们自定义的中间件依赖于request对象,我们一定要放在AuthenticationMiddleware的后面。注册好中间件后,如果你运行 python manage.py runserver 你就会看到输出。

参考链接

本文作者:Eric

本文链接:

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