Flask的核心机制!关于请求处理流程和上下文

学习一样东西不能只停留在表面,我们要探索其中的细节,学习作者的编程思想,这样才能更进一步。

关于WSGI

WSGI(全称Web Server Gateway Interface),是为 Python 语言定义的Web服务器Web应用程序之间的一种简单而通用的接口,它封装了接受HTTP请求解析HTTP请求发送HTTP响应等等的这些底层的代码和操作,使开发者可以高效的编写Web应用。

一个简单的使用WSGI的App例子:

def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, I Am WSGI!</h1>']
  • environ: 一个包含全部HTTP请求信息的字典,由WSGI Server解包HTTP请求生成。
  • start_response: 一个WSGI Server提供的函数,调用可以发送响应的状态码和HTTP报文头, 函数在返回前必须调用一次start_response()
  • application()应当返回一个可以迭代的对象(HTTP正文)。
  • application()函数由WSGI Server直接调用和提供参数。
  • Python内置了一个WSGIREFWSGI Server,不过性能不是很好,一般只用在开发环境。可以选择其他的如Gunicorn
 
WSGI Server 和 App交互图

Flask的上下文对象

Flask有两种Context(上下文),分别是

  • RequestContext 请求上下文
  • Request 请求的对象,封装了Http请求(environ)的内容
  • Session 根据请求中的cookie,重新载入该访问者相关的会话信息。
  • AppContext 程序上下文
  • g 处理请求时用作临时存储的对象。每次请求都会重设这个变量
  • current_app 当前激活程序的程序实例

生命周期:

  • current_app的生命周期最长,只要当前程序实例还在运行,都不会失效。
  • Requestg的生命周期为一次请求期间,当请求处理完成后,生命周期也就完结了
  • Session就是传统意义上的session了。只要它还未失效(用户未关闭浏览器、没有超过设定的失效时间),那么不同的请求会共用同样的session。

Flask处理流程

 
Flask处理请求流程
  • 第一步:创建上下文
    Flask根据WSGI Server封装的请求等的信息(environ)新建RequestContext对象 和AppContext对象
# 声明对象
# LocalStack LocalProxy 都由Werkzeug提供
# 我们不深究他的细节,那又是另外一个故事了,我们只需知道他的作用就行了
# LocalStack 是栈结构,可以将对象推入、弹出
# 也可以快速拿到栈顶对象。当然,所有的修改都只在本线程可见。
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
# 如果调用一个LocalStack实例, 能返回一个 LocalProxy 对象
# 这个对象始终指向 这个LocalStack实例的栈顶元素。
# 如果栈顶元素不存在,访问这个 LocalProxy 的时候会抛出 RuntimeError异常
# LocalProxy对象你只需暂时理解为栈里面的元素即可了
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))
# RequestContext
class RequestContext(object):
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
self.session = None
#AppContext
class AppContext(object):
def __init__(self, app):
self.app = app
self.url_adapter = app.create_url_adapter(None)
self.g = app.app_ctx_globals_class()
self._refcnt = 0

这里需要注意的是,RequestContext在初始化的时候,当前Flask的实例作为参数被传进来。虽然每次的请求处理都会创建一个RequestContext对象,但是每一次传入的app参数却是同一个。通过这个机制,可以使得:

由同一个Flask实例所创建的RequestContext,其成员变量app都是同一个Flask实例对象 。实现了多个RequestContext对应同一个current_app 的目的。

  • 第二步:入栈
    RequestContext对象push进_request_ctx_stack里面。在这次请求期间,访问request对象session对象将指向这个栈的栈顶元素
class RequestContext(object):
def push(self):
....
_app_ctx_stack.push(self)
appcontext_pushed.send(self.app)

AppContext对象push进_app_ctx_stack里面。在这次请求期间,访问g对象将指向这个栈的栈顶元素

class AppContext(object):
def push(self):
....
_request_ctx_stack.push(self)
  • 第三步:请求分发
    response = self.full_dispatch_request()
    Flask将调用full_dispatch_request函数进行请求的分发,之所以不用给参数,是因为我们可以通过request对象获得这次请求的信息。full_dispatch_request将根据请求的url找到对应的蓝本里面的视图函数,并生成一个response对象。注意的是,在请求之外的时间,访问request对象是无效的,因为request对象依赖请求期间的_request_ctx_stack栈。

  • 第四步:上下文对象出栈
    这次HTTP的响应已经生成了,就不需要两个上下文对象了。分别将两个上下文对象出栈,为下一次的HTTP请求做出准备。

  • 第五步:响应WSGI
    调用Response对象,向WSGI Server返回其结果作为HTTP正文。Response对象是一个 可调用对象,当调用发生时,将首先执行WSGI服务器传入的start_response()函数 发送状态码和HTTP报文头。

最后附上Flask处理请求的wsgi_app函数

# environ: WSGI Server封装的HTTP请求信息
# start_response: WSGI Server提供的函数,调用可以发送状态码和HTTP报文头
def wsgi_app(self, environ, start_response):
# 根据environ创建上下文
ctx = self.request_context(environ)
# 把当前的request context,app context绑定到当前的context
ctx.push()
error = None
try:
try:
#根据请求的URL,分发请求,经过视图函数处理后返回响应对象
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
# 最后出栈
ctx.auto_pop(error)

Flask上下文的更多相关文章

  1. Flask上下文管理、session原理和全局g对象

    一.一些python的知识 1.偏函数 def add(x, y, z): print(x + y + z) # 原本的写法:x,y,z可以传任意数字 add(1,2,3) # 如果我要实现一个功能, ...

  2. flask上下文详解

    一.前言 了解过flask的python开发者想必都知道flask中核心机制莫过于上下文管理,当然学习flask如果不了解其中的处理流程,可能在很多问题上不能得到解决,当然我在写本篇文章之前也看到了很 ...

  3. flask上下文全局变量,程序上下文、请求上下文、上下文钩子

    Flask上下文 Flask中有两种上下文,程序上下文(application context)和请求上下文(request context) 当客户端发来请求时,请求上下文就登场了.请求上下文里包含 ...

  4. Flask上下文管理

    一.一些python的知识 1.偏函数 def add(x, y, z): print(x + y + z) # 原本的写法:x,y,z可以传任意数字 add(1,2,3) # 如果我要实现一个功能, ...

  5. Flask 上下文(Context)原理解析

    :first-child { margin-top: 0; } blockquote > :last-child { margin-bottom: 0; } img { border: 0; m ...

  6. Flask上下文管理机制

    前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...

  7. 4.1 python类的特殊成员,偏函数,线程安全,栈,flask上下文

    目录 一. Python 类的特殊成员(部分) 二. Python偏函数 1. 描述 2. 实例一: 取余函数 3. 实例二: 求三个数的和 三. 线程安全 1. 实例一: 无线程,消耗时间过长 2. ...

  8. Flask 上下文机制和线程隔离

    1. 计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决, 上下文机制就是这句话的体现. 2. 如果一次封装解决不了问题,那就再来一次 上下文:相当于一个容器,保存了Flask程序运行过程中 ...

  9. 详解Flask上下文

    上下文是在Flask开发中的一个核心概念,本文将通过阅读源码分享下其原理和实现. Flask系列文章: Flask开发初探 WSGI到底是什么 Flask源码分析一:服务启动 Flask路由内部实现原 ...

  10. 10.Flask上下文

    1.1.local线程隔离对象 不用local对象的情况 from threading import Thread request = ' class MyThread(Thread): def ru ...

随机推荐

  1. webpack简笔(1)

    1.npm init -> 生成package.json文件 2.npm install webpack --save-dev (不建议全局安装 ,会锁定版本) [ --save-dev 开发环 ...

  2. Ireport启动错误

    问题描述:cannot find java.exe Neither (null)\jre\bin\java.exe nor (null)\bin\java.exe exits 解决办法: 打开Irep ...

  3. yii2.0 邮件发送如何配置

    邮件发送配置: 打开配置文件将下面代码添加到 components => [...]中(例:高级版默认配置在/common/config/main-local.php)         'mai ...

  4. 32. pt-visual-explain

    mysql -e "explain select name from db01.t01 where code>1" |pt-visual-explain echo " ...

  5. Java 调用执行其他语言的程序

    以 Java 调用 Python 为例 1. 使用 Runtime 类 该方式简单,但是增加了 Java 对python 的依赖,需要事先安装python,及python程序依赖的第三方库 Runti ...

  6. Android学习(四)

    教材学习内容总结 图形和定制视图 硬件加速 Android APILevel14及其以上版本为目标的应用程序来说,硬件加速是默认可用的. 可通过android:hardwareAccelerated= ...

  7. PowerDesigner code、name显示设置 及 同时显示办法

    菜单->Tool->Model Options->Name Convention->右侧display中选择显示name还是code. 不支持同时显示,但可以选择显示code, ...

  8. oracle 不等於的問題<> !=

    簡單的查詢 SELECT * FROM sap_zcsp WHERE 欄位<>'X' 顯示不出為null的數據,必須帶上 IS null 查詢后得知:null 只能通過IS null 或者 ...

  9. python3 第二十八章 - 内置函数之List相关

    Python包含以下函数: 序号 函数 实例 1 list.append(obj)在列表末尾添加新的对象   2 list.count(obj)统计某个元素在列表中出现的次数   3 list.ext ...

  10. mysql锁机制(Innodb引擎)

    InnoDB实现了两种类型的行锁. 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同的数据集的排他锁. 排他锁(X):允许获得排他锁的事务更新数据,但是组织其他事务获得相同数据集的共享锁和排他 ...