Flask源码解析:Flask应用执行流程及原理
WSGI
WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述服务器端如何与web应用程序通信的规范。
Web应用程序的本质就是:
- 浏览器向服务器发送请求
- 服务器接受客户端请求,并解析
- 服务器端把HTML作为响应体发送给浏览器
- 浏览器拿取响应体渲染网页
在客户端和服务器端WSGI负责协议的转化,WSGI将web组件分为三部分:Web服务器、Web中间件、Web应用程序,当服务器接受到HTTP请求时,会按照WSGI协议解析成Request对象并调用WSGI Application,最后将响应返回给浏览器。
Werkzeug
Werkzeug是Python的WSGI规范的实用函数库。Flask使用的底层WSGI库就是Werkzeug。

WSGI 中有一个非常重要的概念:每个 Python web 应用都是一个可调用(callable)的对象。在 Flask 中,这个对象就是 app = Flask(__name__) 创建出来的 app,就是图中绿色部分。
要运行Web应用,必须依赖于Web Server,比如我们常见的Apache、Nginx、Lighttpd以及我们Flask使用的Werkzug位于黄色部分。
WSGI规定了server和app之间如何进行通信,它规定了app(environ, start_response)接口,environ是环境设置的字典,包含了请求的所有信息,start_response是WSGI处理完毕后调用的函数。
源码位置:werkzeug.serving:WSGIRequestHandler中execute()。
HelloWorld启动流程
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello World"
if __name__ == "__main__":
app.run()
应用启动的代码: app.run()
def run(self, host=None, port=None, debug=None,
load_dotenv=True, **options): """
... 部分代码省略
"""
_host = '127.0.0.1'
_port = 5000
server_name = self.config.get('SERVER_NAME')
sn_host, sn_port = None, None if server_name:
sn_host, _, sn_port = server_name.partition(':') host = host or sn_host or _host
port = int(port or sn_port or _port) from werkzeug.serving import run_simple try:
# 导入werkzeug.serving的run_simple(),传入接受到的参数
# 注意:第三个参数是self: 就是我们创建的Flask app
run_simple(host, port, self, **options)
finally:
self._got_first_request = False
run_simple(host, port, self, **options)
监听指定的IP和端口,当接受到请求时,WSGI会解析,然后调用app去执行请求处理的逻辑。对应的逻辑在werkzeug.serving:WSGIRequestHandler的execute()中:
def execute(app):
# 调用代码获取结果
application_iter = app(environ, start_response)
try:
for data in application_iter:
write(data)
if not headers_sent:
write(b'')
finally:
if hasattr(application_iter, 'close'):
application_iter.close()
application_iter = None
可以看到application_iter = app(environ, start_response),调用app执行获取响应结果。
要调用app实例,那么会调用其__call__()方法。flask.app:Flask:
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response) def wsgi_app(self, environ, start_response): # 下篇博客讲 Flask上下文会解释,先忽略
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push() # 正确的请求路径,会通过路由分发到响应的视图处理函数
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None # 不管处理是否发生异常,都需要把栈中的请求 pop 出来
ctx.auto_pop(error)
上面代码业务逻辑就是通过路由配置,找到具体处理业务的视图函数。full_dispatch_request()相关代码:
def full_dispatch_request(self):
"""
分派请求并在此之上执行请求处理
"""
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)
在这段中核心就是 self.dispatch_request() :
def dispatch_request(self):
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule = req.url_rule
if getattr(rule, 'provide_automatic_options', False) \
and req.method == 'OPTIONS':
return self.make_default_options_response()
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args)
self.dispatch_request() 返回的是处理函数的返回结果(比如Hello World 例子中返回的字符串),finalize_request 会把它转换成 Response 对象。
在 dispatch_request 之前我们看到 preprocess_request,之后看到 finalize_request,它们里面包括了请求处理之前和处理之后的很多 hooks:
before_first_request 、before_request、 after_request 、teardown_request
Flask源码解析:Flask应用执行流程及原理的更多相关文章
- Mybatis 系列10-结合源码解析mybatis 的执行流程
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
- Flask源码解析:Flask上下文
一.上下文(Context) 什么是上下文: 每一段程序都有很多外部变量.只有像Add这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行, ...
- flask源码解析之上下文为什么用栈
楔子 我在之前的文章<flask源码解析之上下文>中对flask上下文流程进行了详细的说明,但是在学习的过程中我一直在思考flask上下文中为什么要使用栈完成对请求上下文和应用上下文的入栈 ...
- Yii2 源码分析 入口文件执行流程
Yii2 源码分析 入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...
- flask源码解析之上下文
引入 对于flask而言,其请求过程与django有着截然不同的流程.在django中是将请求一步步封装最终传入视图函数的参数中,但是在flask中,视图函数中并没有请求参数,而是将请求通过上下文机制 ...
- Flask源码分析二:路由内部实现原理
前言 Flask是目前为止我最喜欢的一个Python Web框架了,为了更好的掌握其内部实现机制,这两天准备学习下Flask的源码,将由浅入深跟大家分享下,其中Flask版本为1.1.1. 上次了解了 ...
- [Java多线程]-线程池的基本使用和部分源码解析(创建,执行原理)
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 多线 ...
- 源码解析flask的路由系统
源码解析flask的路由系统 当我们新建一个flask项目时,pycharm通常已经为项目定义了一个基本路由 @app.route('/') def hello_world(): return 'He ...
- flask源码解析之session
内容回顾 cookie与session的区别: 1. session 是保存在服务端的键值对 2. cookie 只能保存4096个字节的数据,但是session不受限制 3. cookie保存在浏览 ...
随机推荐
- Beta版本冲刺(一)
目录 组员情况 组员1(组长):胡绪佩 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示组内最新成果 团 ...
- C语言和go语言之间的交互 - C语言中使用go语言,使用的go语言又使用了c语言
一.go语言中使用C语言 go代码中使用C代码,在go语言的函数块中,以注释的方式写入C代码,然后紧跟import “C” 即可在go代码中使用C函数 代码示例: go代码:testC.go 1 pa ...
- CentOS下 NFS的简单使用以及windows 关在linux的NFS存储方法
1. 全部安装的情况下NFS已经安装到服务器上面了,如果没有安装的话 需要使用如下命令进行安装 yum -y install nfs-utils rpcbind 2. 创建需要使用的NFS目录 mkd ...
- IOS AES加密之ECB128模式
1.AES加密模式有好几种,网上大多是CBC.256模式,找了好久才找到解决ECB128模式加密. AES需要导入头文件 #import <CommonCrypto/CommonCryptor. ...
- poj2082 Terrible Sets(单调栈)
Description Let N be the set of all natural numbers {0 , 1 , 2 , . . . }, and R be the set of all re ...
- python--inspect模块
inspect模块主要提供了四种用处: 1.对是否是模块.框架.函数进行类型检查 2.获取源码 3.获取类或者函数的参数信息 4.解析堆栈 一.type and members 1. inspect. ...
- 1019C Sergey's problem(思维)
题意: 找出来一个点集S 使得S中的点不能互相通过一步到达 并且S中的点 可以在小于等于2的步数下到达所有的点 要父结点 不要子结点 这样就求出来一个点集S‘ 而S'中可能存在 v -> u ...
- Spring点滴九:Spring bean的延迟初始化
Spring bean延迟初始化: 官网API: By default, ApplicationContext implementations eagerly create and configure ...
- 荷马史诗 NOI2015 解析
比较简单,这道题需要贪心解决. 不需要任何复杂的数据结构,一个luo的堆就足够了. 本题的意思就是:给定n种单词及在文本中各自出现的频率,要求利用二进制串对其进行前缀编码,使得压缩后的文本长度最短. ...
- Linq查询条件里有可空字段比较时报错variable '<>h__TransparentIdentifier2' of type referenced from scope '', but it is not defined
当我运行下面的linq查询时报了这个错, 1: var result = (from so in svcContext.new_sales_orderSet 2: join soitem in s ...