深入探究 Flask 的应用和请求上下文
目标
读完本文后,您应该能够解释:
- 什么是上下文
- 哪些数据同时存储在应用程序和请求上下文中
- 在 Flask 中处理请求时,处理应用程序和请求上下文所需的步骤
- 如何使用应用程序和请求上下文的代理
- 如何在视图函数中使用
current_app和代理request - 什么是上下文本地
什么是上下文?
为了执行你编写的代码,需要处理数据。这些数据可能是配置数据、输入数据、数据库数据等等。
上下文用于跟踪代码需要执行的数据。
在 Flask 中,上下文用于提供处理请求和命令行界面 (CLI) 命令所需的数据。
虽然本文重点介绍处理请求,但所提出的概念也适用于 CLI 命令。
请求处理
让我们从高层的角度开始了解请求的处理方式:

因此,浏览器会向 Web 服务器(如 Nginx 或 Apache)发送请求,以请求特定的 URL(上图中的“/”URL)。然后,Web 服务器会将此请求路由到 WSGI 服务器进行处理。
WSGI 代表 Web 服务器网关接口,是 Web 服务器和基于 Python 的 Web 应用程序之间的接口。它是必需的,因为 Web 服务器无法直接与 Python 应用程序通信。有关详细信息,请查看WSGI。
WSGI 服务器告诉 Flask 应用程序处理请求。
Flask 应用程序生成一个响应,该响应被发送回 WSGI 服务器,再发送回 Web 服务器,最终发送回 Web 浏览器。
这些步骤描述了请求-响应周期,这是通过 Web 服务器、WSGI 应用服务器和 Web 应用程序处理请求的关键功能。
Flask 中的上下文
当收到请求时,Flask 提供两个上下文:
语境 描述 可用对象 应用 跟踪应用程序级数据(配置变量、记录器、数据库连接) current_app,g要求 跟踪请求级数据(URL、HTTP 方法、标头、请求数据、会话信息) request,session值得注意的是,上述每个对象通常被称为“代理”。这只是意味着它们是对象全局风格的代理。我们稍后会深入探讨这一点。
Flask 在收到请求时处理这些上下文的创建。它们可能会造成混淆,因为您并不总是能够根据应用程序所处的状态访问特定对象。
概览图
下图说明了处理请求时如何处理上下文:

第 1 步 - Web 和 WSGI 服务器
当 Web 服务器收到请求时,一切就开始了:

Web 服务器的工作是将传入的 HTTP 请求路由到WSGI服务器。
Apache和Nginx是两种常见的 Web 服务器,而Gunicorn、uWSGI和mod_wsgi是流行的 WSGI 服务器。
值得注意的是,虽然Flask 开发服务器是一个 WSGI 服务器,但它并不适合用于生产。
步骤 2 - 工作者
为了处理该请求,WSGI 服务器会生成一个工作进程来处理该请求:

工作线程可以是线程、进程或协程。例如,如果您使用 Flask Development Server 的默认配置,则工作线程将是线程。
如果您有兴趣了解有关 Python 中线程、多处理和异步之间的更多区别,请查看使用并发、并行和异步加速 Python文章和Python 中的并发视频。
对于这个解释,工作者类型并不重要;关于工作者的关键点是它一次处理一个请求(因此需要多个工作者)。
步骤 3 - 上下文
一旦执行切换到 Flask 应用程序,Flask 就会创建应用程序和请求上下文并将它们推送到各自的堆栈上:

回顾一下,应用程序上下文存储应用程序级数据,例如配置变量、数据库连接和记录器。同时,请求上下文存储需要处理以生成响应的特定于请求的数据。
可能会令人惊讶地看到,但两个堆栈都是作为全局对象实现的(这将在下一节中变得更加清晰)。
步骤 4 - 代理
现在 Flask 应用程序已准备好处理数据(在视图函数中),并且数据已在应用程序和请求上下文堆栈中准备就绪,我们需要一种方法来连接这两部分......代理来救援!

视图函数使用代理来访问应用程序(存储在应用程序上下文堆栈中)和请求上下文(存储在请求上下文堆栈中):
current_app- 代理工作者的应用程序上下文request- 代理工作者的请求上下文
乍一看,这个序列似乎令人困惑,因为视图函数似乎正在通过代理访问全局对象(应用程序和请求上下文堆栈)。如果是这样的话,这个操作就会有问题,因为它不是线程安全的。您可能还会认为这些堆栈(作为全局对象)可以被任何工作程序访问,这将是一个安全问题。
然而,这种设计是 Flask 的一大特色……堆栈被实现为上下文本地对象。
有关代理的更多信息,请查看Flask 文档中的代理注释和代理模式文章。
上下文局部变量
Python 有一个线程本地数据的概念,用于存储特定于线程的数据,它既是“线程安全的,又是线程唯一的”。换句话说,每个线程都能够以线程安全的方式访问数据,并且数据对于特定线程始终是唯一的。
Flask 实现了类似的行为(上下文本地),但是以更通用的方式允许工作者成为线程、进程或协程。
上下文局部变量实际上是在Werkzeug中实现的,它是 Flask 的关键包之一。为简单起见,我们在讨论上下文局部变量时将引用 Flask。
当数据存储在上下文本地对象中时,数据以只有一个工作进程可以检索的方式存储。因此,如果两个单独的工作进程访问上下文本地对象,它们将各自获取各自独有的特定数据。
下一节将介绍一个使用上下文本地对象的示例。
总而言之,每个视图函数中都有current_app和request代理,它们用于从各自的堆栈访问上下文,这些堆栈存储为上下文本地对象。
在应用程序和请求上下文堆栈中使用“堆栈”使这个概念比原来更加令人困惑。这些“堆栈”通常存储的上下文不超过一个。
使用的数据结构是堆栈,因为存在非常高级的场景(例如,内部重定向)需要多于一个的元素。
Flask 中代理的好处
如果你要从头开始创建自己的 Web 框架,那么你可能会考虑将应用程序和请求上下文传递到每个视图函数中,如下所示:
@app.route('/add_item', methods=['GET', 'POST'])
def add_item(application_context, request_context): # contexts passed in!if request_context.method == 'POST':# Save the form data to the database...application_context.logger.info(f"Added new item ({ request_context.form['item_name'] })!")...
事实上,许多 Web 框架都是这样工作的(包括Django)。
然而,Flask 提供了current_app和request代理,它们最终看起来像视图函数的全局变量:
from flask import current_app, request@app.route('/add_item', methods=['GET', 'POST'])
def add_item():if request.method == 'POST':# Save the form data to the database...current_app.logger.info(f"Added new item ({ request.form['item_name'] })!")...
通过使用这种方法,视图函数不需要将上下文作为参数传入;这种方法简化了视图函数定义。但它可能会引起混淆,因为您并不总是能够访问current_app和request代理,具体取决于您的应用程序所处的状态。
提醒:
current_app和request代理实际上不是全局变量;它们指向作为上下文本地实现的全局对象,因此代理对于每个工作者来说始终是唯一的。
第 5 步 - 清理
生成响应后,请求和应用程序上下文将从各自的堆栈中弹出:

此步骤清理堆栈。
然后将响应发送回 Web 浏览器,完成对该请求的处理。
上下文局部变量
上下文本地对象是使用本地对象实现的,可以像这样创建:
$ python>>> from werkzeug.local import Local
>>> data = Local()
>>> data.user = 'pkennedy@hey.com'
每个上下文(即上一节中讨论的“工作者”)都可以访问一个Local对象,用于上下文独有的数据存储。所访问的数据对于上下文来说是唯一的,并且只能由该上下文访问。
LocalStack对象与Local对象类似,但是保留一个对象堆栈以允许push()和pop()操作。
LocalStack在上一节中,我们了解了在 Flask 中处理请求时如何使用应用程序上下文堆栈和请求上下文堆栈。这些堆栈在 Flask 中作为全局内存中的对象实现。
为了帮助巩固上下文局部变量的工作原理,让我们通过一个例子来说明如何LocalStack在全局内存中创建一个对象,然后让三个独立的线程访问它:

以下是该示例的完整脚本:
"""
Example script to illustrate how a global `LocalStack` object can be used
when working with multiple threads.
"""
import random
import threading
import timefrom werkzeug.local import LocalStack# Create a global LocalStack object for storing data about each thread
thread_data_stack = LocalStack()def long_running_function(thread_index: int):"""Simulates a long-running function by using time.sleep()."""thread_data_stack.push({'index': thread_index, 'thread_id': threading.get_native_id()})print(f'Starting thread #{thread_index}... {thread_data_stack}')time.sleep(random.randrange(1, 11))print(f'LocalStack contains: {thread_data_stack.top}')print(f'Finished thread #{thread_index}!')thread_data_stack.pop()if __name__ == "__main__":threads = []# Create and start 3 threads that each run long_running_function()for index in range(3):thread = threading.Thread(target=long_running_function, args=(index,))threads.append(thread)thread.start()# Wait until each thread terminates before the script exits by# 'join'ing each threadfor thread in threads:thread.join()print('Done!')
该文件创建一个LocalStack对象(thread_data_stack)用于存储将要创建的每个线程的数据。
thread_data_stack模仿 Flask 中的应用程序上下文堆栈或请求上下文堆栈。
long_running_function在每个线程中运行:
def long_running_function(thread_index: int):"""Simulates a long-running function by using time.sleep()."""thread_data_stack.push({'index': thread_index, 'thread_id': threading.get_native_id()})print(f'Starting thread #{thread_index}... {thread_data_stack}')time.sleep(random.randrange(1, 11))print(f'LocalStack contains: {thread_data_stack.top}')print(f'Finished thread #{thread_index}!')thread_data_stack.pop()
该函数将有关线程的数据推送到thread_data_stack全局内存中的对象:
thread_data_stack.push({'index': thread_index, 'thread_id': threading.get_native_id()})
此操作模仿将应用程序或请求上下文推送到其各自的堆栈。
函数完成后time.sleep(),将访问以下数据thread_data_stack:
print(f'LocalStack contains: {thread_data_stack.top}')
此操作模仿使用
app_context和request代理,因为这些代理访问其各自堆栈顶部的数据。
在函数的末尾,数据从中弹出thread_data_stack:
thread_data_stack.pop()
此操作模拟从各自的堆栈中弹出应用程序或请求上下文。
脚本运行时会启动3个线程:
# Create and start 3 threads that each run long_running_function()
for index in range(3):thread = threading.Thread(target=long_running_function, args=(index,))threads.append(thread)thread.start()
并且join每个线程都等待,直到每个线程完成执行:
# Wait until each thread terminates before the script exits by
# 'join'ing each thread
for thread in threads:thread.join()
让我们运行这个脚本看看会发生什么:
$ python app.pyStarting thread #0... <werkzeug.local.LocalStack object at 0x109cebc40>
Starting thread #1... <werkzeug.local.LocalStack object at 0x109cebc40>
Starting thread #2... <werkzeug.local.LocalStack object at 0x109cebc40>
LocalStack contains: {'index': 0, 'thread_id': 320270}
Finished thread #0!
LocalStack contains: {'index': 1, 'thread_id': 320271}
Finished thread #1!
LocalStack contains: {'index': 2, 'thread_id': 320272}
Finished thread #2!
Done!
每个线程真正有趣的是它们都指向LocalStack内存中的同一个对象:
Starting thread #0... <werkzeug.local.LocalStack object at 0x109cebc40>
Starting thread #1... <werkzeug.local.LocalStack object at 0x109cebc40>
Starting thread #2... <werkzeug.local.LocalStack object at 0x109cebc40>
当每个线程访问 时thread_data_stack,访问都是该线程独有的LocalStack!这就是(和)的魔力Local——它们允许上下文独有的访问:
LocalStack contains: {'index': 0, 'thread_id': 320270}
LocalStack contains: {'index': 1, 'thread_id': 320271}
LocalStack contains: {'index': 2, 'thread_id': 320272}
与典型的全局内存访问不同,对的访问
thread_data_stack也是线程安全的。
结论
Flask 的一个强大(但令人困惑)的方面是如何处理应用程序和请求上下文。希望本文能对这个主题提供一些澄清!
应用程序和请求上下文在处理请求或 CLI 命令时提供必要的数据。确保使用current_app和request代理来访问应用程序上下文和请求上下文。
相关文章:
深入探究 Flask 的应用和请求上下文
目标 读完本文后,您应该能够解释: 什么是上下文哪些数据同时存储在应用程序和请求上下文中在 Flask 中处理请求时,处理应用程序和请求上下文所需的步骤如何使用应用程序和请求上下文的代理如何在视图函数中使用current_app和代理request什么…...
C++学习笔记(30)
二十三、随机数 在实际开发中,经常用到随机数,例如:纸牌的游戏洗牌和发牌、生成测试数据等。 函数原型: void srand(unsigned int seed); // 初始化随机数生成器(播种子)。 int rand(); // 获一个取随机数。…...
Rust GUI框架 tauri V2 项目创建
文章目录 Tauri 2.0创建应用文档移动应用开发 Android 前置要求移动应用开发 iOS 前置要求参考资料 Tauri 2.0 Tauri 是一个构建适用于所有主流桌面和移动平台的轻快二进制文件的框架。开发者们可以集成任何用于创建用户界面的可以被编译成 HTML、JavaScript 和 CSS 的前端框架…...
C++继承(上)
1.继承的概念 继承是一个类继承另外一个类,称继承的类为子类/派生类,被继承的类称为父类/基类。 比如下面两个类,Student和Person,Student称为子类,Person称为父类。 #include<iostream> using namespace std…...
在 Vim 中打开文件并快速查询某个字符
在 Vim 中打开文件并快速查询某个字符,可以按照以下步骤操作: 打开 Vim 并加载文件: vim your_file.txt将 your_file.txt 替换为你要查询的文件名。 进入普通模式(如果你还在插入模式或其他模式下): Es…...
oracle 条件取反
在Oracle数据库中,条件取反主要通过逻辑运算符NOT来实现。NOT是一个单目运算符,用于对指定的条件表达式取反。当条件表达式为真(True)时,NOT运算符的结果就是假(False);反之…...
力扣最热一百题——缺失的第一个正数
目录 题目链接:41. 缺失的第一个正数 - 力扣(LeetCode) 题目描述 示例 提示: 解法一:标记数组法 1. 将非正数和超出范围的数替换 2. 使用数组下标标记存在的数字 3. 找到第一个未标记的位置 4. 为什么时间复杂…...
零基础入门AI:一键本地运行各种开源大语言模型 - Ollama
什么是 Ollama? Ollama 是一个可以在本地部署和管理开源大语言模型的框架,由于它极大的简化了开源大语言模型的安装和配置细节,一经推出就广受好评,目前已在github上获得了46k star。 不管是著名的羊驼系列,还是最新…...
3.接口测试的基础/接口关联(Jmeter工具/场景一:我一个人负责所有的接口,项目规模不大)
一、Jmeter接口测试实战 1.场景一:我一个人负责所有的接口:项目规模不大 http:80 https:443 接口文档一般是开发给的,如果没有那就需要抓包。 请求默认值: 2.请求: 请求方式:get,post 请求路径 请求参数 查询字符串参数…...
【matlab】将程序打包为exe文件(matlab r2023a为例)
文章目录 一、安装运行时环境1.1 安装1.2 简介 二、打包三、打包文件为什么很大 一、安装运行时环境 使用 Application Compiler 来将程序打包为exe,相当于你使用C编译器把C语言编译成可执行程序。 在matlab菜单栏–App下面可以看到Application Compiler。 或者在…...
从底层原理上解释clickhouse查询为什么快
ClickHouse 是一个开源的列式数据库管理系统,以其极高的查询性能著称。为了理解 ClickHouse 查询为什么快,我们需要从以下几个方面进行深入探讨,包括其架构设计、存储引擎、索引结构、并行化策略以及内存管理等底层原理。 1. 列式存储&#…...
FEAD:fNIRS-EEG情感数据库(视频刺激)
摘要 本文提出了一种可用于训练情绪识别模型的fNIRS-EEG情感数据库——FEAD。研究共记录了37名被试的脑电活动和脑血流动力学反应,以及被试对24种情绪视听刺激的分类和维度评分。探讨了神经生理信号与主观评分之间的关系,并在前额叶皮层区域发现了显著的…...
标准库标头 <bit>(C++20)学习
<bit>头文件是数值库的一部分。定义用于访问、操作和处理各个位和位序列的函数。例如,有函数可以旋转位、查找连续集或已清除位的数量、查看某个数是否为 2 的整数幂、查找表示数字的最小位数等。 类型 endian (C20) 指示标量类型的端序 (枚举) 函数 bit_ca…...
redis群集三种模式:主从复制、哨兵、集群
redis群集有三种模式 redis群集有三种模式,分别是主从同步/复制、哨兵模式、Cluster,下面会讲解一下三种模式的工作方式,以及如何搭建cluster群集 ●主从复制:主从复制是高可用Redis的基础,哨兵和集群都是在主从复制…...
【MATLAB源码-第225期】基于matlab的计算器GUI设计仿真,能够实现基础运算,三角函数以及幂运算
操作环境: MATLAB 2022a 1、算法描述 界面布局 计算器界面的主要元素分为几大部分:显示屏、功能按钮、数字按钮和操作符按钮。 显示屏 显示屏(Edit Text):位于界面顶部中央,用于显示用户输入的表达式和…...
基于yolov8的红外小目标无人机飞鸟检测系统python源码+onnx模型+评估指标曲线+精美GUI界面
【算法介绍】 基于YOLOv8的红外小目标无人机与飞鸟检测系统是一项集成了前沿技术的创新解决方案。该系统利用YOLOv8深度学习模型的强大目标检测能力,结合红外成像技术,实现了对小型无人机和飞鸟等低空飞行目标的快速、准确检测。 YOLOv8作为YOLO系列的…...
网络封装分用
目录 1,交换机 2,IP 3,接口号 4,协议 分层协议的好处: 5,OSI七层网络模型. 6,TCP/IP五层网络模型(主流): [站在发送方视角] [接收方视角] 1,交换机 交换机和IP没有关系,相当于是对路由器接口的扩充,这时相当于主机都与路由器相连处于局域网中,把越来越多的路由器连接起…...
【Finetune】(一)、transformers之BitFit微调
文章目录 0、参数微调简介1、常见的微调方法2、代码实战2.1、导包2.2、加载数据集2.3、数据集处理2.4、创建模型2.5、BitFit微调*2.6、配置模型参数2.7、创建训练器2.8、模型训练2.9、模型推理 0、参数微调简介 参数微调方法是仅对模型的一小部分的参数(这一小部分可…...
ubuntu24系统普通用户免密切换到root用户
普通用户登录系统后需要切换到root用户,这边需要密码,现在不想让用户知道密码是多少。 sudo: 1 incorrect password attempt $ su - Password: root-security-cm5:~#开始配置普通用户免密切换到root用户,编辑配置文件 /etc/sudoers 最后增加…...
如何应对pcdn技术中遇到的网络安全问题?
在应对网络安全问题时,需要采取一系列的操作措施,以确保网络环境的稳定性和数据的安全性。以下是一些建议: 选择可靠的PCDN提供商:与有良好安全记录的PCDN提供商合作,确保提供商具备专业的安全团队,能够提…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
