Django(18):中间件原理和使用

目录

  • 概述
  • Django自带中间件
  • Django的中间件执行顺序
  • 自定义中间件
    • 函数
    • 使用类
  • 其它中间件钩子函数
    • process_view
    • process_exception
    • process_template_response
    • 如何使用这3个钩子函数?
  • 全局异常处理
  • 小结

概述

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

HTTP Web服务器工作原理一般都是接收用户发来的请求(request), 然后给出响应(response)。Django也不例外,其一般工作方式是接收request请求和其它参数,交由视图(view)处理,然后给出它的响应(response): 渲染过的html文件或json格式的数据。然而在实际工作中Django并不是接收到request对象后,马上交给视图函数或类(view)处理,也不是在view执行后立马把response返回给用户。**一个请求在达到视图View处理前需要先经过一层一层的中间件处理,经过View处理后的响应也要经过一层一层的中间件处理才能返回给用户 **。

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

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

中间件常用于权限校验、限制用户请求、打印日志、改变输出内容等多种应用场景,比如:

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

注意:装饰器也经常用于用户权限校验。但与装饰器不同,中间件对Django的输入或输出的改变是全局的。比如@login_required装饰器仅作用于单个视图函数。如果你希望实现全站只有登录用户才能访问,编写一个中间件是一个更好的解决方案。

Django自带中间件

当你创建一个新Django项目时,你会发现settings.py里的MIDDLEWARE列表已经注册了一些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会话支持;
  • CommonMiddleware:基于APPEND_SLASH和PREPEND_WWW的设置来重写URL,如果APPEND_SLASH设为True,并且初始URL 没有以斜线结尾以及在URLconf 中没找到对应定义,这时形成一个斜线结尾的新URL;
  • CsrfViewMiddleware:添加跨站点请求伪造的保护,通过向POST表单添加一个隐藏的表单字段,并检查请求中是否有正确的值;
  • AuthenticationMiddleware:在视图函数执行前向每个接收到的user对象添加HttpRequest属性,表示当前登录的用户,无它用不了request.user
  • MessageMiddleware:开启基于Cookie和会话的消息支持
  • XFrameOptionsMiddleware:对点击劫持的保护

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

Django的中间件执行顺序

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

在这里插入图片描述

自定义中间件

自定义中间件你首先要在app所属目录下新建一个文件middleware.py, 添加好编写的中间件代码,然后在项目settings.py中把它添加到MIDDLEWARE列表进行注册,添加时一定要注意顺序。

Django提供了两种编写自定义中间件的方式:函数和类,基本框架如下所示:

函数

def simple_middleware(get_response):# 一次性设置和初始化def middleware(request):# 请求在到达视图前执行的代码response = get_response(request)# 响应在返回给客户端前执行的代码return responsereturn middleware

当请求从浏览器发送到服务器视图时,将执行response = get_response(request)该行之前的所有代码。当响应从服务器返回到浏览器时,将执行response = get_response(request)此行之后的所有内容。

那么这条分界线respone = get_response(request)做什么的?简而言之,它将调用列表中的下一个中间件。如果这是最后一个中间件,则将调用该视图。

示例

我们现在以函数编写一个名为timeit_middleware的中间件,打印出执行每个请求所花费的时间,代码如下所示:

import timedef timeit_middleware(get_response):def middleware(request):start = time.time()response = get_response(request)end = time.time()print("请求花费时间: {}秒".format(end - start))return responsereturn 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','blog.middleware.timeit_middleware', # 新增
]

执行效果

每当Django处理一个请求时,终端(terminal)就会打印出请求花费时间。

使用类

class SimpleMiddleware:def __init__(self, get_response):# 一次性设置和初始化self.get_response = get_responsedef __call__(self, request):# 视图函数执行前的代码response = self.get_response(request)# 视图函数执行后的代码return response

示例

我们现在以类来编写一个名为LoginRequiredMiddleware的中间件,实现全站要求登录,但是登录页面和开放白名单上的urls除外。代码如下所示:

from django.shortcuts import redirect
from django.conf import settingsclass LoginRequiredMiddleware:def __init__(self, get_response):self.get_response = get_responseself.login_url = settings.LOGIN_URL# 开放白名单,比如['/login/', '/admin/']self.open_urls = [self.login_url] + getattr(settings, 'OPEN_URLS', [])def __call__(self, request):        if not request.user.is_authenticated and request.path_info not in self.open_urls:return redirect(self.login_url + '?next=' + request.get_full_path())response = self.get_response(request) return response

小知识: request.path_info用于获取当前请求的相对路径,如/articles/,而request.get_full_path()用于获取当前请求完整的相对路径,包括请求参数,如/articles/?page=2。使用request.get_full_path()时别忘了加括号哦,否则返回的是uwsgi请求对象,不是字符串。

注册中间件

修改settings.py, 注册中间件,并添加 LOGIN_URLOPEN_URLS

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','blog.middleware.timeit_middleware','blog.middleware.LoginRequiredMiddleware',
]LOGIN_URL = "/admin/login/"
OPEN_URLS = ["/admin/"]

查看效果

添加完中间件后,你访问任何非LOGIN_URL和OPEN_URLS里的urls,都需要你先进行登录。

其它中间件钩子函数

Django还提供了其它三个中间件钩子函数,分别在执行视图函数,处理异常和进行模板渲染时调用。

process_view

process_view(request, view_func, view_args, view_kwargs)

该方法有四个参数

  • request是HttpRequest对象。
  • view_func是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。
  • view_args是将传递给视图的位置参数的列表。
  • view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。

Django会在调用视图函数之前调用process_view方法。它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

process_exception

process_exception(self, request, exception)

该方法两个参数:

  • 一个HttpRequest对象
  • 一个exception是视图函数异常产生的Exception对象。

这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。该方法常用于发生异常时通知管理员或将其日志的形式记录下来。

process_template_response

process_template_response(self, request, response)

该方法两个参数:

  • 一个HttpRequest对象
  • 一个response是TemplateResponse对象(由视图函数或者中间件产生)。

该方法是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象)。该方法常用于向模板注入变量或则直接改变模板。

如何使用这3个钩子函数?

在函数或类的中间件中应该如何使用上面3个钩子函数呢? 具体实现方式如下:

函数

from django.http import HttpResponsedef timeit_middleware(get_response):def middleware(request):response = get_response(request)return responsedef process_view(request, view_func, view_args, view_kwargs)return None or HttpResponse(xx)def process_exception(self, request, exception):return None or HttpResponse(xx)def process_template_response(self, request, response)return ...middleware.process_view = process_viewmiddleware.process_exception = process_exceptionmiddleware.process_template_response = process_template_responsereturn middleware

class MyClassMiddleware:def __init__(self, get_response):self.get_response = get_responsedef __call__(self, request):return self.get_response(request)def process_view(request, view_func, view_args, view_kwargs)return None or HttpResponse(xx)def process_exception(self, request, exception):return None or HttpResponse(xx)# 例子: 打印出异常return HttpResponse(<h1>str(exception)</h1)# 该方法仅对TemplateResponse输入有用,对render方法失效def process_template_response(self, request, response)response.context_data['title'] = 'New title'return response

全局异常处理

利用process_exception实现全局异常处理。

首先创建一个中间件

import traceback
from django.http import JsonResponse
import logginglogger = logging.getLogger(__name__)class ExceptionMiddleware:def __init__(self, get_response):self.get_response = get_responsedef __call__(self, request):response = self.get_response(request)return responsedef process_exception(self, request, exception):traceback_info = traceback.format_exc()logger.info(f"request_path: {request.path}, traceback_info: {traceback_info}")return JsonResponse({"code": -1, "msg": "error"}, status=500)

使用 traceback.format_exc() 函数获取到 exception 的报错信息,然后通过 logger 日志打印输出。

这里主要输出两个信息,一个是接口请求的路径,request.path,一个是报错信息 traceback_info,当然,这里我们还可以记录更多的信息,比如请求的用户信息,请求的参数等。

最后返回报错:eturn 了 response,还有一个 http 的状态码 status=500,这些信息都是可以自己拟定的。

定义好之后就是调用中间件,settings.py 里去引用这个中间件:

MIDDLEWARE = [...'hunter.middlewares.exception_middleware.ExceptionMiddleware',
]

小结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/137412.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Learn Prompt-Prompt 高级技巧:Agents 组件详解

在以LLM驱动的Agent系统中&#xff0c;LLM扮演着Agent的大脑角色&#xff0c;并辅以几个关键组件&#xff1a; 规划&#xff1a;LLM能够进行全面的规划&#xff0c;不仅仅是简单的任务拆分。它可以评估不同的路径和策略&#xff0c;制定最佳的行动计划&#xff0c;以实现用户给…

MongoDB【部署 02】mongodb使用配置文件启动、添加为系统服务及自启动(一个报错:[13436][NotMasterOrSecondary])

MongoDB使用配置文件启动、添加为系统服务及设置自启动 1.是什么2.下载安装启动配置2.1 下载2.2 安装2.3 配置2.4 使用配置文件启动 3.设置系统服务及自启动3.1 设置为系统服务3.2 自启动 1.是什么 【以下内容来自ChatGPT3.5】 MongoDB是一个流行的开源文档型数据库管理系统&a…

41. Linux系统配置FTP服务器并在QT中使用QFtp实现文件上传

1. 说明 这篇博客主要记录一些在Linux系统中搭建FTP服务器时踩过的一些坑,以及在使用QFtp上传文件时需要注意的问题。 2. FTP环境搭建 在linux系统中,需要安装vsftpd,可以在终端中输入下面的命令进行安装: sudo apt-get install vsftpd使用上述命令安装后,系统中会有一…

ChunJun(OldNameIsFlinkX)

序言 ChunJun主要是基于Flink实时计算框架,封装了不同数据源之间的数据导入与导出功能.我们只需要按照ChunJun的要求提供原始与目标数据源的相关信息给Chunjun,然后它会帮我们生成能运行与Flink上的算子任务执行,这样就避免了我们自己去根据不同的数据源重新编辑读入与读出的方…

学习笔记|模数转换器|ADC原理|STC32G单片机视频开发教程(冲哥)|第十七集:ADC采集

文章目录 1.模数转换器&#xff08;ADC&#xff09;是什么&#xff1f;手册说明&#xff1a; 2.STC32G单片机ADC使用原理19.1.1 ADC控制寄存器&#xff08;ADC_CONTR)19.1.2 ADC配置寄存器&#xff08;ADCCFG)19.1.4ADC时序控制寄存器&#xff08;ADCTIM&#xff09;19.3 ADC相…

CSS盒子模型、列表样式

盒子模型 常用的html标签都可以看作一个盒子&#xff0c;称为盒子模型 盒子由四部分组成&#xff1a; content、padding、border、margin 边框 border:border-width&#xff08;粗细&#xff09; | border-style(样式) | border-color&#xff08;颜色&#xff09; #one{bor…

云原生的简单理解

一、何谓云原生&#xff1f; 一种构建和运行应用软件的方法 应用程序从设计之初即考虑到云的环境&#xff0c;原生为云而设计&#xff0c;在云上以最佳姿势运行&#xff0c;充分利用和发挥云平台的弹性分布式优势。 二、包括以下四个要素 采用容器化部署&#xff1a;实现云平…

蓝牙核心规范(V5.4)10.6-BLE 入门笔记之L2CAP

蓝牙篇之蓝牙核心规范(V5.4)深入详解汇总 1.概述 L2CAP负责协议复用、流量控制、服务数据单元(SDU)的分段和重组。它使用通道的概念来分隔在堆栈层之间传递的数据包序列。固定通道不需要设置,立即可用,并与特定的上层协议相关联。通道也可以通过指定的协议服务多路复用器…

湖南衡阳3D扫描在生物仿真研究的应用高精度三维扫描螃蟹-CASAIM中科广电

生物仿真研究与应用一直是科研及工艺品的热门方向&#xff0c;很多设计脱胎于生物本身&#xff0c;传统方式又大多只能以画师手绘为主&#xff0c;做到“纤毫毕现”极其困难&#xff0c;故而才有了“齐白石的虾”、“徐悲鸿的马”等出圈的艺术家的画作&#xff0c;对于某种生物…

深度学习论文: ISTDU-Net:Infrared Small-Target Detection U-Net及其PyTorch实现

深度学习论文: ISTDU-Net&#xff1a;Infrared Small-Target Detection U-Net及其PyTorch实现 ISTDU-Net&#xff1a;Infrared Small-Target Detection U-Net PDF: https://doi.org/10.1109/LGRS.2022.3141584 PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTo…

jvm-sandbox-repeater源码解析-配置管理

一、配置初见 源码里提供的控制台截图如下&#xff1a;&#xff08;怎么搭建自己去百度&#xff09; 从中取出对应的配置如下&#xff1a; { "degrade": false, //阻断能力 "exceptionThreshold": 1000, //异常采样率 "httpEntrancePatterns&qu…

SunTorque亮相GAF2023数字化智能装配工程与装备技术大会

智能扭矩系统-智能拧紧系统-智能扭矩控制-SunTorque GAF2023数字化智能装配工程与装备技术大会在中国上海汽车会展中心盛大开幕&#xff0c;青创智通与装配领域、智能制造、数字化应用等相关先进智造技术的知名企业一齐亮相。 本次展会&#xff0c;我们带来了扭矩相关解决方案…

Vivado IP中Generate Output Products的设置说明

文章目录 Vivado IP中Generate Output Products的设置说明Synthesis OptionsRun Settings 官方文档中的介绍Generate Output ProductsSynthesis Options for IP 参考文献 Vivado IP中Generate Output Products的设置说明 在创建IP核时&#xff0c;将IP核的信息配置完成之后会弹…

数据预处理方式合集

删除空行 #del all None value data_all.dropna(axis1, howall, inplaceTrue) 删除空列 #del all None value data_all.dropna(axis0, howall, inplaceTrue) 缺失值处理 观测缺失值 观测数据缺失值有一个比较好用的工具包——missingno&#xff0c;直接传入DataFrame&…

面向面试知识-Redis

面向面试知识-Redis 什么是Redis 运行于内存的基于key-value的非关系型数据库。 一款开源的内存数据结构存储&#xff0c;用作数据库、缓存、消息代理等。&#xff08;可以基于Redis实现分布式锁、以及消息队列&#xff09; 发布订阅&#xff1f;&#xff1f; 对数据类型的操…

【操作系统笔记】内存分配

内存对齐 问题&#xff1a;为什么需要内存对齐呢&#xff1f; 主要原因是为了兼容&#xff0c;为了让程序可以运行在不同的处理器中&#xff0c;有很多处理器在访问内存的时候&#xff0c;只能从特定的内存地址读取数据。换个说法就是处理器每次只能从内存取出特定个数字节的数…

Spring Boot实现对超大文件进行异步压缩下载

在Web应用中&#xff0c;文件下载功能是一个常见的需求&#xff0c;特别是当你需要提供用户下载各种类型的文件时。本文将演示如何使用Spring Boot框架来实现一个简单而强大的文件下载功能。我们将创建一个RESTful API&#xff0c;通过该API&#xff0c;用户可以下载问价为ZIP压…

Python计算机二级知识点整理

1.当一个进程在运行过程中释放了系统资源后要调用 唤醒进程原语 唤醒进程原语是把进程从等待队列里移出到就绪队列并设置进程为就绪状态&#xff0c;当一个进程在运行过程中释放了系统资源后进入就绪状态&#xff0c;调用唤醒进程原语。 2. 3. 4.在希尔排序法中&#x…

免费开源 | 基于SSM的校园订餐系统

源码下载地址获取 关注并私信回复“订餐”&#xff0c;获取源码下载方式 一定要关注后发消息&#xff0c;否则系统限制无法回复消息 感谢开源&#xff01;侵删&#xff01; 一、功能实现 1. 前台模块 前台主要功能有&#xff1a;用户注册、用户登录、我的购物车、我的订单、…

人类认知的贝叶斯与机器的贝叶斯

贝叶斯原理是一种基于概率的分析方法&#xff0c;可以用来估计一个事件发生的概率。在人类认知和机器学习领域中&#xff0c;都有对应的贝叶斯原理。 人类认知的贝叶斯原理&#xff1a; 在人类认知研究中&#xff0c;贝叶斯原理被认为是一种重要的思维方式。人类的认知过程通常…