对整个上下文管理过程做描述
并对请求到来处理阶段做了详细描述
1. 源码分析
可以根据源码的内容,分析一次请求从到来到结束,源码中到底做了哪些操作
# 1.app开始运行
app运行前,会先执行其__call__方法,进而执行wsgi_app方法
- self.wsgi_app(environ, start_response)
# 2.打包
ctx = self.request_context(environ)
- ctx.request = request_class(environ) = request
- ctx.session = None # 第一次请求session会为空
# 3.将打包好的内容,放到Local对象中
ctx.push() # 分为AC和RC的push两个动作
# 1)app的push操作
- app_ctx = self.app.app_context() # app_ctx = AppContext(app,g)
- app_ctx.push() # app_ctx = LocalStack() 同理,LocalStack中也有一个Local
- AppContext
- __init__
- self.app = app # 封装了一个app,app即当前实例化Flask的对象
- self.g = app.app_ctx_global_class() # 封装了一个g,g可以看作一次请求中的全局变量
# 也就是说,通过_app_ctx_stack(LocalStack)将app_ctx放置到Local对象中
# 2)request的push操作
- _request_ctx_stack.push(self) # 具体放的动作
- _request_ctx_stack = LocalStack() #
- self._local = Local()
- self._local.stack = rv = [] # rv = getattr(self._local, "stack", None) 当rv=None时,放置一个空的列表进去
- storage[ident][name] = value # 执行_local的__setattr__方法
# 也就是说,通过_request_ctx_stack(LocalStack)将ctx放置到Local对象中
数据:
{
请求1号:{"stack":[ctx(request,session),]},
请求2号:{"stack":[ctx(request,session),]}, # 此时session还为空
...请求n号...
}
- self.session = session_interface.open_session(self.app, self.request) # 打开ctx中的session,并为session赋值
# - 路由匹配 会执行self.match_request(),了解即可
# 4.找视图函数
response = self.full_dispatch_request()
- rv = self.preprocess_request() # preprocess_request,解决执行视图函数之前的一些事宜,即执行before_request
- rv = self.dispatch_request() # 执行视图函数
- self.finalize_request(rv) # 执行after_request 和 save_session
- response = self.process_response(response)
- funcs = chain(funcs, reversed(self.after_request_funcs[bp])) # 执行after_request的操作
- self.session_interface.save_session(self, ctx.session, response) # 执行save_session,把现有的值拿来保存到浏览器cookie中
# 5.pop操作
ctx.auto_pop(error)
- self.pop(exc)
- rv = _request_ctx_stack.pop() 执行pop操作,将上述push到栈中的数据pop掉
数据:
{
请求1号:{"stack":[ctx(request,session),]},
请求2号:{"stack":[ctx(request,session),]}, # pop掉
...请求n号...
}
- app_ctx.pop(exc) # 同理
{
请求1号:{"stack":[app_ctx(app,g),]},
请求2号:{"stack":[app_ctx(app,g),]},, # pop掉
...请求n号...
}
上述的源码基本都在app.py
和ctx.py
两个Flaks源码文件中
2. 总结步骤
根据以上源码中的动作,我们大概能把上下文机制分为三个阶段,其中每个阶段又有自己要做的工作,具体如下
# 第一阶段,请求到来处理阶段
# 打包请求数据到ctx(ctx = self.request_context(environ))
# 将ctx(request,session)放到Local对象(ctx.push)
# 第二阶段:视图调用实现具体业务阶段
# 视图函数导入 request/session,执行一系列视图函数定义的操作(self.full_dispatch_request())
# 第三阶段:处理结束返回阶段
# 请求处理完毕,获取session并保存到cookie(save_session操作);
# 并将ctx删除(pop操作)
3. 补充
3.1 Flask中的session是什么时候创建的,又是什么时候销毁的?
- 当请求刚进来时,会将request和session封装成一个
RequestContext
对象,然后通过LocalStack
将ctx放入到一个Local
对象中; - 执行
open_session
操作,读取cookie中的值,拿出来重新赋值到Local对象中的ctx中 - 当返回结果时,执行
save_session
,将ctx中的session读出,并序列化,然后写入到Cookie中(给用户了),然后返回response - 执行
pop
,将ctx从Local中移除
3.2 扩展AC部分
上述的分析,未对视图处理部分做详细描述,下篇博客中会进行补充