Flask上下文管理

请求上下文:

在项目启动时:

_request_ctx_stack对象    (请求上下文对象)

  _request_ctx_stack对象被LocalStack类实例化

class LocalStack(object):
    def __init__(self):
        self._local = Local()    # 实例化local类对象 
。。。。。
LocalStack类

  其中LocalStack类中self._local 又被Local类实例化

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)
Local类

  上述操作为准备过程,为接下来的该线程开辟了一个独立的存储空间

_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__方法

def __call__(self, environ, start_response):

    return self.wsgi_app(environ, start_response)  
View Code

再执行__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,配置相关)

posted @ 2018-03-27 15:35  JAYWX  阅读(154)  评论(0编辑  收藏  举报