在初级Django开发项目中,你大概率用不到中间件(Middleware)。但随着项目需求越来越复杂,你就需要开始编写自己的中间件了。当你了解到Django中间件(middleware)的工作原理和作用之后,你就会知道中间件的灵活和强大之处了。本文着重分析中间件(middleware)的工作原理和应用场景,并提供一些使用自定义中间件的简单例子。注意:本文示例所使用Django版本为Django 3.0。

什么是中间件(middleware)及中间件(middleware)的工作原理

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

上面这段是Django官方文档中对于Middleware的介绍,听上去非常抽象难懂,小编我来尝试用浅显的语言再介绍一遍吧。我们首先要了解下Django的request/response处理机制,然后再看看Middleware在整个处理机制中的角色及其工作原理。

HTTP Web服务器工作原理一般都是接收用户发来的请求(request), 然后给出响应(response)。Django也不例外,其一般工作方式是接收request对象和其它参数,交由视图(view)处理,然后给出它的响应(respone)数据: 渲染过的html文件或json格式的数据。然而在实际工作中Django并不是接收到request对象后,马上交给视图函数或类(view)处理,也不是在view执行后立马给用户返回reponse。事实上Django最初接收的是HttpRequest对象,而不是request对象,正是中间件的作用把HttpRequest对象和user对象打包成了一个全局变量request对象,这样你才可以View中使用request作为变量或者在模板中随意调用request.user。

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

HttpRequest -> Middleware -> View -> Middleware -> HttpResponse

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

  • 禁止特定IP地址的用户或未登录的用户访问我们的View视图函数

  • 对同一IP地址单位时间内发送的请求数量做出限制

  • 在View视图函数执行前记录用户的IP地址

  • 在View视图函数执行前传递额外的变量或参数

  • 在View视图函数执行前或执行后把特定信息打印到log日志

  • 在View视图函数执行后对reponse数据进行修改后返回给用户

值得一提的是中间件对Django的输入或输出的改变是全局的,反之亦然。如果让你希望对Django的输入或输出做出全局性的改变时,需要使用中间件。举个例子,我们在装饰器一文中介绍了如何使用@login_required装饰器要求用户必须先登录才能访问我们的视图函数。试想我们有个网站绝大部分视图函数都需要用户登录,每个视图函数前面都需要加上@login_required装饰器是比较傻的行为。借助于中间件,我们无需使用装饰器即可全局实现:只有登录用户才能访问视图函数,匿名用户跳转到登录页面。实现原理也很简单,在一个request到达视图函数前,我们先对request.user是否验证通过进行判断,然后再进行跳转。另外Django对POST表单中携带的CSRF token的全局校验也是通过CsrfViewMiddleware这个中间件进行的,而不是通过单个装饰器实现的。

Django自带中间件介绍

当你创建一个新django项目时,你会发现settings.py里已经注册了一些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属性,表示当前登录的用户,无它用不了request.user

  • MessageMiddleware:开启基于Cookie和会话的消息支持,无它无message

  • XFrameOptionsMiddleware:对点击劫持的保护

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

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

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

注意:从Django 1.10起, settings.py里注册中间件使用MIDDLEWARE=,而不是MIDDLEWARE_CLASSES= 。

Django的中间件执行顺序

当你在settings.py注册中间件时一定要要考虑中间件的执行顺序,中间件在request到达view之前是从上向下执行的,在view执行完后返回reponse过程中是从下向上执行的,如下图所示。举个例子,如果你自定义的中间件有依赖于request.user(比如判断用户是否登录),那么你自定义的中间件一定要放在AuthenticationMiddleware的后面。

自定义中间件

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

函数实现方式

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 responsereturn middleware

类实现方式

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整个处理过程,打印出请求执行过程,并在视图函数前打印出用户是否登录。

#users/middleware.py

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 你就会看到如下输出:

如果本篇文章你都看懂了并重复了本文代码,那么恭喜你终于学会编写自己的Django中间件啦。注意:本文代码是基于Django 3.0版本的,如果你的django版本是1.x或2.x版本的,你需要按如下方式自定义中间件,并使用MIDDLEWARE_CLASSES注册。

from django.utils.deprecation import MiddlewareMixin
class MyMiddleware(MiddlewareMixin):def process_request(self, request):print("Request before view is called!")def process_response(self, request, response):print("Response after view is called!")return responsedef process_exception(self, request, exception):print("Exception!")

如果你想更深入地学习和了解自定义Django的中间件(Middleware),可以阅读下篇Django高级(1): 自定义中间件(Middleware)详解及示例。注意:这篇文章仅对小部分读者有用,然而写起来很耗时间,所以是要付费的哦。

小结

本文介绍了Django中间件(Middleware)的工作原理,执行顺序及如何自定义中间件。了解中间件一定要先对Django的request/response处理过程非常了解。当你希望在视图函数执行请求前或执行请求后添加额外的功能,且这种功能是全局性的(针对所有的request或view或response), 那么使用中间件是最好的实现方式。

如果你不想错过我们发表的新文章,建议您把我们公众号设为星标哦,操作方式如下所示:

大江狗

2020.3.29

更多阅读

Django基础(31): 如何理解和正确使用Django信号(Signals)

Django基础(30):模型(Models)的继承详解

Django基础(26): 常用装饰器应用场景及正确使用方法

Django基础(8): 缓存Cache应用场景及工作原理,Cache设置及如何使用

Django基础(33): 中间件(middleware)的工作原理和应用场景举例相关推荐

  1. Django 中间件(middleware)的工作原理和应用场景举例

    在初级Django开发项目中,你大概率用不到中间件(Middleware).但随着项目需求越来越复杂,你就需要开始编写自己的中间件了.当你了解到Django中间件(middleware)的工作原理和作 ...

  2. Django基础之中间件

    一:中间件 中间件:django 中的中间件(middleware),在django中,就是一个类.在请求来和结束后,Django会根据自己的规则在合适的时机执行中间件的相应方法: 应用:对所有请求或 ...

  3. 计算机基础原理知识,计算机基础知识之计算机的工作原理

    1.3 计算机的工作原理 到目前为止,微机的工作原理均采用冯.若依曼的存储程序方式,即把程序存储在微机内,由微机自动存取指令并执行它.微机的工作过程就是执行程序的过程,而程序由指令序列组成,因此,执行 ...

  4. OCR的工作原理与应用场景

    OCR 光学符号识别 光学符号识别,即OCR (Optical Character Recognition),是计算机视觉领域的一个重要分支,主要用于将图像中的文本转换为机器可读的形式.20世纪90年 ...

  5. Django 源码: 中间件(middleware)

    BaseHandler 详解 BaseHandler 在 django.core.handlers.base.py 中定义, 有两个核心的成员方法不得不提, 里面就涉及了中间件的信息: # 好经典的 ...

  6. 数据采集:Flume和Logstash的工作原理和应用场景

    在某个Logstash的场景下,我产生了为什么不能用Flume代替Logstash的疑问,因此查阅了不少材料在这里总结,大部分都是前人的工作经验下,加了一些我自己的思考在里面,希望对大家有帮助. 大数 ...

  7. 多线程读取同一个文件_前端进阶:多线程Web Workers的工作原理及使用场景

    Web Worker 概述 Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行.在主线程运行的同时,Worker ...

  8. Django基础(35): 全局上下文处理器(Context Processors)详解及如何自定义模板上下文处理器...

    Django的Context Processors的中文名字有很多种,有人翻译成文本处理器,也有人翻译成上下文处理器.小编最喜欢的翻译是全局上下文处理器,因为它的主要作用就是向模板传递需要全局使用的变 ...

  9. 半导体的基础-三极管的工作原理,史上绝无仅有的理解方式

    半导体的器件的根基:晶体三极管(三极管,mos管等),所有的模拟电路用他们的线性区,所有的数字电路用他们的饱和区和截止区. 本文适合电类学科的大中专及以上的人士阅读,最好是已经学完了模电,但是仍旧不能 ...

最新文章

  1. firefox+linux+nginx搭建server与client通过证书双向认证环境
  2. jpa中使用Query判断条件查询
  3. java打包要依赖maven库吗_maven-将依赖的 jar包一起打包到项目 jar 包中
  4. A*算法解决八数码问题 Java语言实现
  5. java socket通信
  6. mac linux 蓝牙键盘,还在纠结Mac版键盘?试试KeyRemap4MacBook吧!
  7. 团队冲刺第二阶段-9
  8. 2.4 表单数据的验证
  9. apipost脚本使用一
  10. 一文带你了解 JVM 的垃圾回收机制
  11. 对象池 IObjectPool -- ESBasic 可复用的.NET类库(15)
  12. 【.md格式文件编辑器】几款主流好用的markdown编辑器介绍
  13. linux安装moodle最新版,于linux已安装moodle
  14. h3c防火墙服务器ip修改,H3C防火墙常用配置命令
  15. python语句分号_Python中的分号
  16. mysql分区语录_MYSQL分区管理
  17. composer 升级/降级安装包
  18. 群论基础速成(6):五大著名群族
  19. 跟它比,期货简直 Low 爆了!
  20. TCP和UDP的区别有哪些?

热门文章

  1. 用C语言编写函数multiple求倍数、用C语言编写函数isEven判断奇数和偶数
  2. 囧!万恶的微软组策略
  3. 金蝶BOS是什么,能给您带来什么价值?
  4. C++中测数组的长度
  5. win10上启用HEIF文件缩略图显示 - 删除没用的微软内置HEIF组件 - 安装开源免费的三方HEIF组件CopyTransHEIC
  6. mysql答辩会问什么_计算机科学与技术专业,毕设答辩会问什么问题?
  7. Ch支持java不,ch.hsr.geohash包使用
  8. 嵌入式分享合集125
  9. 对图片进行锐化处理,通俗易懂!
  10. 2022年制冷与空调设备运行操作最新解析及制冷与空调设备运行操作作业考试题库