Flask上下文管理
请求上下文:
在项目启动时:
_request_ctx_stack对象 (请求上下文对象)
_request_ctx_stack对象被LocalStack类实例化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class LocalStack(object): def __init__(self): self._local = Local() # 实例化local类对象 。。。。。
其中LocalStack类中self._local 又被Local类实例化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
try: from greenlet import getcurrent as get_ident except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident class Local(object): __slots__ = ('__storage__', '__ident_func__') def __init(self): object.__setattr(self,"__storage__",{}) object.__setattr(self,"__ident_func__",get_ident)
上述操作为准备过程,为接下来的该线程开辟了一个独立的存储空间
_app_ctx_stack对象 (应用上下文对象)
_app_ctx_stack对象被LocalStack类实例化
与上述构造请上下文代码一致
当项目启动后,执行到app.run() 执行Flask类中的run方法
def run(self, host=None, port=None, debug=None, **options): 。。。。。 run_simple(host, port, self, **options) # 其中self就是app,当有请求来访问即执行app()
当项目监听到有请求来访问时,即执行Flask类中的__call__方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def __call__(self, environ, start_response): return self.wsgi_app(environ, start_response)
再执行__call__方法时又去调用Flask中的wsgi_app方法
wsgi_app方法内有中做了很多事儿:有哪些事儿呢?
1 # 对每个请求都要做的事儿 2 def wsgi_app(self, environ, start_response): 3 ctx = self.request_context(environ) # 把请求相关的东西封装到一个RequestContext类的对象中 environ请求相关的所有数据 4 ctx.push() # 把Appcontext对象(其中有g)放到了应用上下文对象中,把RequestContext对象(其中有session和request)放到了请求上下文对象中 5 error = None 6 try: 7 try: 8 response = self.full_dispatch_request() # 寻找视图函数,并执行
9 except Exception as e: 10 error = e 11 response = self.handle_exception(e) 12 except: 13 error = sys.exc_info()[1] 14 raise 15 return response(environ, start_response) 16 finally: 17 if self.should_ignore_error(error): 18 error = None 19 ctx.auto_pop(error)
具体剖析wsgi_app中的代码
ctx = self.request_context(environ) 把请求相关封装到RequestContext类的对象中
实例化RequestContext类对象
1 class RequestContext(object): 2 def __init__(self, app, environ, request=None): 3 self.app = app # 上下文对象的app属性是当前的Flask应用; 4 if request is None: 5 request = app.request_class(environ) # 封装到 Request(environ) 中 6 self.request = request # 上下文对象的request属性是通过Request类构造的实例,反映请求的信息; 7 self.url_adapter = app.create_url_adapter(self.request) 8 # 上下文对象的url_adapter属性是通过Flask应用中的Map实例构造成一个MapAdapter实例,主要功能是将请求中的URL和Map实例中的URL规则进行匹配; 9 self.flashes = None # 消息闪现的信息 10 self.session = None # 上下文对象的session属性存储请求的会话信息
ctx.push() 操作中 把AppContext对象放到应用上下文对象中,把RequestContext对象放到请求上下文对象中,获取session值
1 def push(self): 2 top = _request_ctx_stack.top 3 if top is not None and top.preserved: 4 top.pop(top._preserved_exc) 5 app_ctx = _app_ctx_stack.top 6 7 if app_ctx is None or app_ctx.app != self.app: 8 app_ctx = self.app.app_context() # AppContext(self)对象 app_ctx.app当前app对象 app_ctx.g 9 app_ctx.push() # 把 AppContext对象放到了应用上下文对象中 10 self._implicit_app_ctx_stack.append(app_ctx) 11 else: 12 self._implicit_app_ctx_stack.append(None) 13 14 if hasattr(sys, 'exc_clear'): 15 sys.exc_clear() 16 # self 是 requestContext对象 其中包含了请求相关的所有对象 17 _request_ctx_stack.push(self) # 把 RequestContext对象放到了请求上下文对象中 18 19 self.session = self.app.open_session(self.request) #获取session 20 if self.session is None: 21 self.session = self.app.make_null_session()
response = self.full_dispatch_request() 寻找请求对应的视图函数,并执行
1 def full_dispatch_request(self): 2 self.try_trigger_before_first_request_functions() # 执行 before_first_request装饰的函数 3 try: 4 request_started.send(self) # 触发 request_started 信号 5 rv = self.preprocess_request() # 调用 before_request 6 if rv is None: 7 rv = self.dispatch_request() # 把各url封装到函数url对应表中 8 except Exception as e: 9 rv = self.handle_user_exception(e) 10 return self.finalize_request(rv) # 对返回值进一步处理
=====
此处来个小插曲,即请求扩展中的before_first_request,before_request,after_request装饰器
@app.before_first_request 装饰器
1 def before_first_request(self, f): 2 self.before_first_request_funcs.append(f) # 空列表中加入f函数 3 return f 4 # before_first_request_funcs = []
@app.before_request 装饰器
1 def before_request(self, f): 2 self.before_request_funcs.setdefault(None, []).append(f) # 字典的values值是个列表,加入当前的f函数
3 return f 4 # self.before_request_funcs = {}
@app.after_request 装饰器
1 def after_request(self, f): 2 self.after_request_funcs.setdefault(None, []).append(f) # 字典的values值是个列表,加入当前的f函数
3 return f 4 # self.after_request_funcs = {}
=====
self.try_trigger_before_first_request_functions() 执行 before_first_request装饰的函数
被before_first_request装饰的函数只有在请求第一次来时会执行被装饰的函数,其中就是self._got_first_request 这个变量控制的,最开始值为False
1 def try_trigger_before_first_request_functions(self): 2 if self._got_first_request: # 最开始为false 3 return 4 with self._before_request_lock: 5 if self._got_first_request: 6 return 7 for func in self.before_first_request_funcs: # 循环before_first_request列表 并执行函数 8 func() 9 self._got_first_request = True # 所有before_first_request函数执行完,改为Ture
request_started.send(self) 触发 request_started 信号
rv = self.preprocess_request() 执行before_request 装饰的函数
1 def preprocess_request(self): 2 3 bp = _request_ctx_stack.top.request.blueprint # 找到该请求对应的蓝图 4 5 funcs = self.url_value_preprocessors.get(None, ()) 6 if bp is not None and bp in self.url_value_preprocessors: 7 funcs = chain(funcs, self.url_value_preprocessors[bp]) 8 for func in funcs: 9 func(request.endpoint, request.view_args) 10 11 funcs = self.before_request_funcs.get(None, ()) # 得到befer_request装饰的函数的一个元祖 12 if bp is not None and bp in self.before_request_funcs: 13 funcs = chain(funcs, self.before_request_funcs[bp]) 14 for func in funcs: # 循环 运行before_request装饰的函数 15 rv = func() 16 if rv is not None: 17 return rv
rv = self.dispatch_request() 在url对应表中找到当前url对应的视图函数并运行
1 def dispatch_request(self): 2 req = _request_ctx_stack.top.request 3 if req.routing_exception is not None: 4 self.raise_routing_exception(req) 5 rule = req.url_rule 6 # if we provide automatic options for this URL and the 7 # request came with the OPTIONS method, reply automatically 8 if getattr(rule, 'provide_automatic_options', False) \ 9 and req.method == 'OPTIONS': 10 return self.make_default_options_response() 11 # otherwise dispatch to the handler for that endpoint 12 return self.view_functions[rule.endpoint](**req.view_args) # 执行视图函数
=====
在运行视图函数期间,若函数的返回值是 render_templates时,会触发 before_render_template 信号和 template_rendered 信号
before_render_template.send(app, template=template, context=context)
template_rendered.send(app, template=template, context=context)
=====
self.finalize_request(rv) 对返回值进一步处理
1 def finalize_request(self, rv, from_error_handler=False): 2 response = self.make_response(rv) 3 try: 4 response = self.process_response(response) # 处理after_request装饰函数 session.save 5 request_finished.send(self, response=response) 6 except Exception: 7 if not from_error_handler: 8 raise 9 self.logger.exception('Request finalizing failed with an ' 10 'error while handling an error') 11 return response
response = self.process_response(response) 处理after_request装饰的函数 session.save
1 def process_response(self, response): 2 3 ctx = _request_ctx_stack.top 4 bp = ctx.request.blueprint 5 funcs = ctx._after_request_functions 6 if bp is not None and bp in self.after_request_funcs: 7 funcs = chain(funcs, reversed(self.after_request_funcs[bp])) 8 if None in self.after_request_funcs: 9 funcs = chain(funcs, reversed(self.after_request_funcs[None])) 10 for handler in funcs: # 循环 after_request装饰的函数, 并执行 11 response = handler(response) # 把返回值response的传到下一个 after_request装饰的函数中 12 if not self.session_interface.is_null_session(ctx.session): 13 self.save_session(ctx.session, response) 14 return response
request_finished.send(self, response=response) 触发 request_finished 信号
response = self.handle_exception(e) 如果期间有报错,则获取错误信息
1 def handle_exception(self, e): 2 3 exc_type, exc_value, tb = sys.exc_info() 4 5 got_request_exception.send(self, exception=e) # 触发 got_request_exception.send 异常信号 6 handler = self._find_error_handler(InternalServerError()) 7 8 if self.propagate_exceptions: 9 if exc_value is e: 10 reraise(exc_type, exc_value, tb) 11 else: 12 raise e 13 14 self.log_exception((exc_type, exc_value, tb)) 15 if handler is None: 16 return InternalServerError() 17 return self.finalize_request(handler(e), from_error_handler=True) # 对返回值进一步处理
got_request_exception.send(self, exception=e) 触发 异常信号
response(environ, start_response) 将返回值给用户
request,g,session 在程序运行到 ctx.push()时,把 request,session封装到RequestContext对象中,把g封装到AppContext对象中
request = LocalProxy(partial(_lookup_req_object, 'request')) # 全局变量 程序最开始时执行的 偏函数 session = LocalProxy(partial(_lookup_req_object, 'session')) g = LocalProxy(partial(_lookup_app_object, 'g'))
当视图中调用 request,session时,其实就是在使用 请求上下文对象,调用该线程的唯一标识对应的key为'stack',value为列表中最后一个的RequestContext对象的request/session属性,再做request.方法时,就是执行LocalProxy类的__getattr__方法
当视图中调用g时,就是在使用应用上下文对象,调用调用该线程的唯一标识对应的key为'stack',value为列表中最后一个的AppContext对象的g属性,再执行LocalProxy类的__getattr__方法
注:请求上下文和应用上下文都是LocalStack类的对象,并且他们都封装了Local类对象,他们各自都有自己的Local类对象,不冲突,因为Local类对象其中封装的内容也不同,一个封装的是RequestContext对象(封装着request,session),一个封装的是AppContext对象(封装着g,配置相关)