学习一样东西不能只停留在表面,我们要探索其中的细节,学习作者的编程思想,这样才能更进一步。
关于WSGIWSGI(全称Web Server Gateway Interface),是为 Python 语言定义的Web服务器和Web应用程序之间的一种简单而通用的接口,它封装了接受HTTP请求、解析HTTP请求、发送HTTP,响应等等的这些底层的代码和操作,使开发者可以高效的编写Web应用。一个简单的使用WSGI的App例子:
1 | def application(environ, start_response): |
- environ: 一个包含全部HTTP请求信息的字典,由WSGI Server解包HTTP请求生成。
- start_response: 一个WSGI Server提供的函数,调用可以发送响应的状态码和HTTP报文头, 函数在返回前必须调用一次start_response()。
- application()应当返回一个可以迭代的对象(HTTP正文)。application()函数由WSGI Server直接调用和提供参数。
- Python内置了一个WSGIREF的WSGI Server,不过性能不是很好,一般只用在开发环境。可以选择其他的如Gunicorn。
Flask的上下文对象
Flask有两种Context
(上下文),分别是
RequestContext
请求上下文Request
请求的对象,封装了Http请求(environ
)的内容Session
根据请求中的cookie,重新载入该访问者相关的会话信息。AppContext
程序上下文g
处理请求时用作临时存储的对象。每次请求都会重设这个变量current_app
当前激活程序的程序实例
生命周期:
current_app
的生命周期最长,只要当前程序实例还在运行,都不会失效。Request
和g
的生命周期为一次请求期间,当请求处理完成后,生命周期也就完结了Session
就是传统意义上的session了。只要它还未失效(用户未关闭浏览器、没有超过设定的失效时间),那么不同的请求会共用同样的session。
Flask处理流程
- 第一步:创建上下文
Flask根据WSGI Server封装的请求等的信息(environ)新建RequestContext对象 和AppContext对象
1 |
|
1 |
|
1 |
|
这里需要注意的是,RequestContext在初始化的时候,当前Flask的实例作为参数被传进来。虽然每次的请求处理都会创建一个RequestContext对象,但是每一次传入的app参数却是同一个。通过这个机制,可以使得:
由同一个Flask实例所创建的RequestContext,其成员变量app都是同一个Flask实例对象 。实现了多个RequestContext对应同一个current_app 的目的。
- 第二步:入栈
将RequestContext对象push进_request_ctx_stack里面。在这次请求期间,访问request对象,session对象将指向这个栈的栈顶元素1
2
3
4
5
6
class RequestContext(object):
def push(self):
....
_app_ctx_stack.push(self)
appcontext_pushed.send(self.app)
AppContext对象push进_app_ctx_stack里面。在这次请求期间,访问g对象将指向这个栈的栈顶元素
1 |
|
第三步:请求分发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函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 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框架-上下文对象 :Flask核心机制
Werkzeug(Flask)之Local、LocalStack和LocalProxy