总结一下flask框架的请求处理流程。
系列文章flask基础之安装和使用入门(一)
flask基础之jijia2模板使用基础(二)
flask基础之jijia2模板语言进阶(三)
flask基础之app初始化(四)
WSGI协议一般来说http服务器和框架需要进行解耦,http专门负责接受HTTP请求、解析HTTP请求、发送HTTP,响应请求等;而web框架负责处理请求的逻辑,和数据库的交互等等,那么它们之间需要约定一套接口使得http服务器能够调用web框架的处理逻辑,这个协议就是WSGI协议。
WSGI协议要求http服务器接收到http请求后经过处理得到两个参数,一个是请求数据封装的字典environ,另一个是需要框架回调的方法start_response。
在flask框架中,服务器对每个请求调用一次app的wsgi_app方法返回结果,而wsgi_app方法的执行过程就是请求的处理流程。
class Flask(object): def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) ctx.push() error = None try: try: 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 ctx.auto_pop(error) 第一步:服务器启动服务器启动后,假设服务器是基于线程的,此时app对象被创建,加载了相关的初始化参数,这时代理对象如current_app、g、session、request等会被创建,但是它们目前并没有代理任何的对象,如果此时使用它们会报错,需要在第一次接收到请求后才会真正地代理上下文。那么服务器启动究竟干了什么事呢?
详细请参考:flask之app初始化
第二步:接收请求,创建上下文,入栈服务器收到一个http请求后,使用app上下文和请求数据创建一个线程,调用app的request_context(self, environ)方法,将解包后封装的http请求数据当做environ参数传入,返回一个RequestContext实例对象,每一个请求都有一个RequestContext实例对象,同时他们都拥有各自的app上下文,也就是说在本线程中的app应用是服务器初始化app的一个引,因此我们可以动态修改app的属性。
将RequestContext对象push进_request_ctx_stack里面,_request_ctx_stack是一个栈对象,此时代理对象request指向栈顶的RequestContext对象的request属性,该request是一个Request对象,而session此时指向栈顶的RequestContext对象的session属性。
判断_app_ctx_stack栈顶是否存在应用上下文对象AppContext,不存在就创建,同时将AppContext推送到_app_ctx_stack栈对象中,此时current_app指向栈顶AppContext对象的app属性,而g变量指向栈顶AppContext对象的g属性,本质上是一个_AppCtxGlobals对象,数据结构是一个字典。
应用上下文和请求上下文存放的栈对象
_request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack()动态修改app的属性
from flask import Flask app = Flask(__name__) @app.route('/test1') def test1(): """ 动态添加一个视图函数 """ @app.route('/test2') def test2(): return 'test2' return 'OK'应用上下文和请求上下文源码分析
class Flask(object): def app_context(self): return AppContext(self) def request_context(self, environ): return RequestContext(self, environ) 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() def push(self): self._refcnt += 1 if hasattr(sys, 'exc_clear'): sys.exc_clear() _app_ctx_stack.push(self) # 将自己推送到栈中 appcontext_pushed.send(self.app) 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 def push(self): pass 第三步:请求分派分发请求并执行处理逻辑的函数为full_dispatch_request,其返回一个Response对象。处理的过程为:
先执行app对象before_first_request_funcs列表中的所有方法,这是针对app的第一次请求需要的预处理方法,执行该列表中的所有方法是一个原子操作,被加了线程锁,如果不是第一次请求就跳过;
然后执行app对象的url_value_preprocessors字典中对应蓝图的列表中的所有方法,对所有的URL进行预处理;
执行app对象的before_request_funcs列表中的所有方法,其会按照加载的顺序链执行,并且如果中间有任何一个方法返回的结果不是None,那么执行中断,直接返回结果,不再执行视图函数。这是针对app所有的请求都会执行的方法,当然也可以通过蓝图来进行管理;
通过request对象的url_rule(Rule)找到app中的url_map中对应的视图函数执行,返回一个元组的结果rv,就是我们平时写视图函数时返回的元组;
调用make_response函数,以返回的结果rv作为参数构建一个Response对象;