flask 是如何分发请求的?

这篇博客会涉及一些 WSGI 的知识,不了解的可以看这篇博客,简单了解一下。

Python 的 WSGI 简单入门

一、请求在 flask 中的处理过程

我们先来看一下 werkzeug.routing 包下 Map 和 Rule 方法的使用,这里给出一个官方的示例(我进行了一点修改并增加了简单的运行代码):

from werkzeug.routing import Map, Rule, Subdomain, NotFound, RequestRedirecturl_map = Map([Rule('/', endpoint='blog/index'),Rule('/<int:year>', endpoint='blog/archive'),Rule('/<int:year>/<int:month>/', endpoint='blog/archive'),Rule('/<int:year>/<int:month>/<int:day>', endpoint='blog/archive'),Rule('/<int:year>/<int:month>/<int:day>/<slug>', endpoint='blog/show_post'),Rule('/about', endpoint='blog/about_me'),Rule('/feeds', endpoint='blog/feeds'),Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
])def application(environ, start_response):urls = url_map.bind_to_environ(environ)try:endpoint, args = urls.match()except HTTPException as e:return e(environ, start_response)start_response('200 OK', [('Content-Type', 'text/plain')])rsp = f'Rule points to {endpoint!r} with arguments {args!r}'return [rsp.encode('utf-8')] # 直接返回字符串会报错,这里进行一次转换# provide a basic wsgi for test!
if __name__ == '__main__':from wsgiref.simple_server import make_serverwith make_server('', 8000, application) as httpd:print("Listening on port 8000....")httpd.serve_forever()

flask 的底层也是依赖于 Map 和 Rule,所以我们使用 @route 或者 add_url_rule 最终的目的也是构建类似上面的 url_map 对象,只不过它更加易用。有趣的是,这里并没有 view_func 函数,所以我们是统一返回了 200 OK,不过 urls.match 的参数也表明了我们得到的是 endpoint,这是我们通过它来查找到对应 view_func 的关键信息。

要注意这里的 urls.match() 的返回值中这个 args 是指 url 中的定义的参数,下面是几个示例:

urls = m.bind("example.com", "/")
urls.match("/", "GET")
# ('index', {})
urls.match("/downloads/42")
# ('downloads/show', {'id': 42})

1.1 add_url_rule 方法 和 @route 装饰器

add_url_rule: Connects a URL rule. Works exactly like the :meth:route decorator. If a view_func is provided it will be registered with the endpoint.
连接 URL 规则。其工作原理与 route 装饰器完全相同。如果提供了 view_func 函数,它会被用 endpoint 来注册。

基础示例:

@app.route('/')
def index():pass

等价于以下:

def index():passapp.add_url_rule('/', 'index', index)

如果没有提供 view_func 函数,需要手动绑定 endpointview_func 函数。

app.view_function['index'] = index

在内部,route 方法会调用 add_url_rule 方法。

下面我们来看源码,这里我进行了删减,对于我认为不重要的部分去掉,我认为这样可以节约理解设计思路的脑力。

@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None,provide_automatic_options=None, **options):# 这里上下省略部分代码,只保留我认为关键的代码if endpoint is None:endpoint = _endpoint_from_view_func(view_func)rule = self.url_rule_class(rule, methods=methods, **options)self.url_map.add(rule)if view_func is not None:old_func = self.view_functions.get(endpoint)if old_func is not None and old_func != view_func:raise AssertionError('View function mapping is overwriting an ''existing endpoint function: %s' % endpoint)self.view_functions[endpoint] = view_func

说明:首先如果 endpoint 为空,则会使用 view_func 函数的名字,接着使用 add_url_rule 函数的参数创建 Rule 对象,将其加入 self.url_map 中,这是一个 Map 对象。然后会将 endpoint 作为键, view_func 作为值,存入 self.view_functions 中,它是一个 dict 对象。

也就是说我们最终得到了下面两个对象,它们是 Flask 类的两个实例属性。还记得上面的 urls.match 方法吗?当我们获取到 endpoint 后,就可以它为键在 slef.view_functions 中去索引对应的 view_func 函数,然后用它来执行对应路由的请求。

class Flask(_PackageBoundObject):def __init__(self,import_name,static_url_path=None,static_folder='static',static_host=None,host_matching=False,subdomain_matching=False,template_folder='templates',instance_path=None,instance_relative_config=False,root_path=None):#: The :class:`~werkzeug.routing.Map` for this instance.self.url_map = Map()#: A dictionary of all view functions registered. The keys will #: be function names which are also used to generate URLs and #: the values are the function objects themselves.#: To register a view function, use the :meth:`route` decorator.self.view_functions = {}

route 只是一个方便的装饰器函数,本质上还是调用 add_url_rule 函数。

def route(self, rule, **options):"""A decorator that is used to register a view function for agiven URL rule.  This does the same thing as :meth:`add_url_rule`but is intended for decorator usage::@app.route('/')def index():return 'Hello World'"""def decorator(f):endpoint = options.pop('endpoint', None)self.add_url_rule(rule, endpoint, f, **options)return freturn decorator

2.1 Flask 中的请求处理过程

我们创建的 Flask 的实例,最终也是类似于上面的 application 被 wsgi 服务调用,只是更加复杂一些,下面就来看看简化的流程:

class Flask(_PackageBoundObject):def __call__(self, environ, start_response):"""The WSGI server calls the Flask application object as theWSGI application. This calls :meth:`wsgi_app` which can bewrapped to applying middleware."""return self.wsgi_app(environ, start_response)def wsgi_app(self, environ, start_response):"""The actual WSGI application. This is not implemented in:meth:`__call__` so that middlewares can be applied withoutlosing a reference to the app object. Instead of doing this::app = MyMiddleware(app)It's a better idea to do this instead::app.wsgi_app = MyMiddleware(app.wsgi_app)Then you still have the original application object around andcan continue to call methods on it."""ctx = self.request_context(environ)              # 创建请求上下文error = Nonetry:try:ctx.push()                               # 推入请求上下文response = self.full_dispatch_request()  # 分派请求except Exception as e:error = eresponse = self.handle_exception(e)except:error = sys.exc_info()[1]raisereturn response(environ, start_response)     # 响应客户端finally:if self.should_ignore_error(error):error = Nonectx.auto_pop(error)                          # 弹出,防止积压,造成资源泄漏def full_dispatch_request(self):"""Dispatches the request and on top of that performs requestpre and postprocessing as well as HTTP exception catching anderror handling... versionadded:: 0.7"""self.try_trigger_before_first_request_functions()try:request_started.send(self)rv = self.preprocess_request()      if rv is None:rv = self.dispatch_request()   # 这里前后增加了一些资源处理操作,except Exception as e:                 # 不过不是我们关注的重点,只看rv = self.handle_user_exception(e) # 这一行业务相关的即可return self.finalize_request(rv)def dispatch_request(self):"""Does the request dispatching.  Matches the URL and returns thereturn value of the view or error handler.  This does not have tobe a response object.  In order to convert the return value to aproper response object, call :func:`make_response`."""req = _request_ctx_stack.top.request        # 获取当前请求的信息if req.routing_exception is not None:self.raise_routing_exception(req)rule = req.url_rule                         # 获取到 url 对象# if we provide automatic options for this URL and the# request came with the OPTIONS method, reply automaticallyif getattr(rule, 'provide_automatic_options', False) \and req.method == 'OPTIONS':return self.make_default_options_response()             # 从 view_function 中找到endpoint对应的# otherwise dispatch to the handler for that endpoint       # view_func 函数,通过视图参数调用它并返回结果,return self.view_functions[rule.endpoint](**req.view_args)  # 注意这里返回的并非响应对象。def finalize_request(self, rv, from_error_handler=False):"""Given the return value from a view function this finalizesthe request by converting it into a response and invoking thepostprocessing functions.  This is invoked for both normalrequest dispatching as well as error handlers.Because this means that it might be called as a result of afailure a special safe mode is available which can be enabledwith the `from_error_handler` flag.  If enabled, failures inresponse processing will be logged and otherwise ignored.:internal:"""response = self.make_response(rv)                   # 视图函数的返回结果被传入了这里,并转化成响应对象try:                                                # 关于这个 response 对象,这里就不往下继续了,下面response = self.process_response(response)      # 已经很抽象了,我觉得了解到这里即可。request_finished.send(self, response=response)except Exception:if not from_error_handler:raiseself.logger.exception('Request finalizing failed with an ''error while handling an error')return response

总结:flask 实例通过请求的 URL 来查找对应的 endpoint,再通过它来查找到对应的视图函数 view_func,然后传入视图函数的参数进行请求处理。在调用视图函数之前,它已经把请求上下文推入了,所以我们在视图函数中可以自由的使用它们,这就是 flask 处理一个请求的大致过程。

关于请求上下文中的全局变量,也就是 request 这些的使用,可以阅读这篇博客:

对 flask 框架中的全局变量 request 探究

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

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

相关文章

怎么获取免费的 GPU 资源完成大语言模型(LLM)实验

怎么获取免费的 GPU 资源完成大语言模型(LLM)实验 目录 怎么获取免费的 GPU 资源完成大语言模型(LLM)实验在线平台类Google ColabKaggle NotebooksHugging Face Spaces百度飞桨 AI Studio在线平台类 Google Colab 特点:由 Google 提供的基于云端的 Jupyter 笔记本环境,提…

Python开发Django面试题及参考答案

目录 Django 的请求生命周期是怎样的? Django 的 MTV 架构中的各个组件分别是什么? Django 的 URL 路由是如何工作的? Django 的视图函数和视图类有什么区别? Django 的模板系统是如何渲染 HTML 的? Django 的 ORM 是如何工作的? Django 的中间件是什么?它的作用是…

【图像的读写与基本操作】

图像的读写与基本操作 目录 图像的读写与基本操作目标知识点1. 图像的读写 &#xff1a;2. 图像的缩放 &#xff1a;3. 图像的翻转 &#xff1a;4. 图像的裁剪 &#xff1a;5. 颜色空间转换 &#xff1a; 示例代码1. 图像的读写 &#xff1a;2. 图像的缩放 &#xff1a;3. 图像…

《数字图像处理》笔记

文章目录 第一章 绪论1.1 什么是数字图像处理数字图像的概念数字图像的组成数字图像处理的概念 1.4 数字图像处理的基本步骤 第二章 数字图像基础2.2 光和电磁波谱可见光单色光灰度级发光强度光通量亮度 2.3 图像感知和获取将照射能量变换为数字图像的传感器简单的图像形成模型…

网络安全扫描--基础篇

前言 1、了解互联网安全领域中日趋重要的扫描技术 2、了解在不同网络场景下扫描技术手段 3、熟悉linux下系统内核防护策略并能大件一个有效的系统防护体系 4、增强工作安全意识&#xff0c;并能有效的实践于工作场景中 目录 1、熟悉主机扫描工具&#xff08;fping&#xff0c;…

前端防重复请求终极方案:从Loading地狱到精准拦截的架构升级

&#x1f525; 事故现场还原&#xff1a;疯狂点击引发的血案 凌晨1点23分&#xff0c;监控系统突然告警&#xff1a; &#x1f4c9; 服务器CPU飙升至98% &#x1f5c3;️ 数据库出现3000脏数据 &#x1f4a5; 用户端弹出上百个错误弹窗 事故原因&#xff1a;黑产脚本通过0.5秒…

基于Spring Boot的供应商管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Redis|事务

文章目录 是什么能干嘛Redis 事务 VS 数据库事务怎么玩小总结 是什么 首先回想一下什么是数据库的事务&#xff1f;数据库事务是指作为单个逻辑单元执行的一系列操作&#xff0c;具备以下四个关键特性&#xff08;ACID&#xff09;&#xff1a; 原子性&#xff08;Atomicity&am…

一周学会Flask3 Python Web开发-Jinja2模板继承和include标签使用

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 不管是开发网站还是后台管理系统&#xff0c;我们页面里多多少少有公共的模块。比如博客网站&#xff0c;就有公共的头部&…

二十三种设计模式详解

二十三种设计模式是软件开发中用于解决常见问题的经典解决方案&#xff0c;它们由 Erich Gamma 等四位作者在《设计模式&#xff1a;可复用面向对象软件的基础》一书中提出。这些模式分为三大类&#xff1a;创建型模式、结构型模式 和 行为型模式。 1. 创建型模式&#xff08;…

用pyside6创建一个界面并实现一个小功能且能打包成问题记录

现在我们要开发一个程序&#xff0c;让用户输入一段文本包含&#xff1a;员工姓名、薪资、年龄。该程序可以把薪资在 2万 以上、以下的人员名单分别打印出来。 1用designer创建界面并生成UI文件&#xff1b; 2直接调用ui文件实现功能&#xff1b; from PySide6.QtWidgets im…

计算机毕业设计 ——jspssm510springboot 的人职匹配推荐系统

作者&#xff1a;程序媛9688 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等。 &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff08;免费咨询指导选题&#xff09;&#xf…

包子凑数——蓝桥杯真题Python

包子凑数 输入输出样例 示例 1 输入 2 4 5输出 6样例说明 凑不出的数目包括&#xff1a;1, 2, 3, 6, 7, 11。 示例 2 输入 2 4 6输出 INF样例说明 所有奇数都凑不出来&#xff0c;所以有无限多个 运行限制 最大运行时间&#xff1a;1s最大运行内存: 256M 最大公约数 最大公…

SSM和SpringBoot有什么区别?

SSM&#xff08;Spring、Spring MVC、MyBatis&#xff09;和 Spring Boot 有以下一些区别&#xff1a; 配置方式 SSM&#xff1a;配置相对繁琐&#xff0c;需要在多个 XML 文件中进行大量的配置。Spring Boot&#xff1a;采用“约定大于配置”的原则&#xff0c;极大地简化了配…

极简Python服务器后端

在Python中&#xff0c;可以使用http.server模块和json模块来创建一个简单的HTTP服务器&#xff0c;该服务器可以响应80端口上的/query POST请求&#xff0c;并且请求体为JSON格式。 需要注意&#xff0c;在Linux系统上&#xff0c;使用低于1024的端口&#xff08;如80端口&…

文档检索服务平台

文档检索服务平台是基于Elasticsearch的全文检索&#xff0c;包含数据采集、数据清洗、数据转换、数据检索等模块。 项目地址&#xff1a;Github、国内Gitee 演示地址&#xff1a;http://silianpan.cn/gdss/ 以下是演示角色和账号&#xff08;密码同账号&#xff09;&#xf…

关于Postman自动获取token

在使用postman测试联调接口时&#xff0c;可能每个接口都需要使用此接口生成的令牌做Authorization的Bearer Token验证&#xff0c;最直接的办法可能会是一步一步的点击&#xff0c;如下图&#xff1a; 在Authorization中去选择Bearer Token&#xff0c;然后将获取到的token粘贴…

清华大学DeepSeek文档下载,清华大学deepseek下载(完成版下载)

文章目录 前言一、清华大学DeepSeek使用手册下载二、清华大学DeepSeek使用手册思维导图 前言 这是一篇关于清华大学deepseek使用手册pdf的介绍性文章&#xff0c;主要介绍了DeepSeek的定义、功能、使用方法以及如何通过提示语设计优化AI性能。以下是对这些核心内容的简要概述&…

Linux:(3)

一&#xff1a;Linux和Linux互传&#xff08;压缩包&#xff09; scp:Linux scp 命令用于 Linux 之间复制文件和目录。 scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令。 scp 是加密的&#xff0c;rcp 是不加密的&#xff0c;scp 是…

【新人系列】Python 入门专栏合集

✍ 个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12801353.html &#x1f4e3; 专栏定位&#xff1a;为 0 基础刚入门 Python 的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们…