flask请求上下文 及相关源码
flask上下文对象: 1.RequestContext请求上下文: Request:request请求对象,封装了http请求的内容 Session:根据请求中的cookie,重新载入该访问者相关的会话信息 2.AppContext程序上下文: g对象:处理请求时用作临时存储的对象 current_app:当前激活程序的程序实例 3.生命周期: 1.current_app的生命周期最长,只要当前程序实例还在运行,都不会失效 2.Request和g的生命周期为一次请求期间,当请求处理完成后,生命周期也就完结了 3.Session就是传统意义上的session,只要还未失效,那么不同的请求可以访问到同一个session
源码: 1. 全局的变量 _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, "request")) session = LocalProxy(partial(_lookup_req_object, "session")) g = LocalProxy(partial(_lookup_app_object, "g")) 2. flask请求上下文全部与源码: def wsgi_app(self, environ, start_response): #environ请求相关的所有数据,ctx就是ResquestContext的对象,包含request ctx = self.request_context(environ) error = None try: try: #把request放进去了,Local() ctx.push() #执行视图函数,请求扩展 response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise #返回响应 return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ##把request,和session删除 ctx.auto_pop(error) 2.1 ctx = self.request_context(environ),这个environ是请求相关的 self.request_context(environ)这个话的本质是执行return RequestContext(self, environ) 现在传给RequestContext的变量self是当前app,environ是请求相关的 2.1.1我们发现RequestContext是一个类,类加括号是就实例化得到对象, 这个对象里面有request,session等: app-->就当前的flask对象,environ请求相关的 def __init__(self, app, environ, request=None, session=None): self.app = app if request is None: request = app.request_class(environ) self.request = request self.url_adapter = None try: self.url_adapter = app.create_url_adapter(self.request) except HTTPException as e: self.request.routing_exception = e self.flashes = None self.session = session self.preserved = False self._preserved_exc = None self._after_request_functions = [] 2.1.2 这样我们执行ctx = self.request_context(environ), 得到是RequestContext的对象这个对象里面有request,session等, 2.2 ctx.push()的源码: 执行的是RequestContext里面的push方法: _request_ctx_stack.push(self),当前的self是ctx 我们发现_request_ctx_stack就是LocalStack() , _request_ctx_stack.push(self)就是执行LocalStack的push,并且把ctx传过来了 2.2.1 _request_ctx_stack.push(self)的源码: #obj就是ctx,self._local是Local对象,用来区分不同线程,协程的数据 def push(self, obj): rv = getattr(self._local, "stack", None) if rv is None: # self._local=>stack-->storage['线程id']['stack']=[] self._local.stack = rv = [] # self._local=>stack-->storage['线程id']['stack']=[ctx,] rv.append(obj) return rv 3. request是怎样取到我放到 Local对象里面的ctx在里面的request request = LocalProxy(partial(_lookup_req_object, "request")) 当我们request.form时候就会找LocalProxy(partial(_lookup_req_object, "request"))要form属性 3.1当要request属性时候,就会执行LocalProxy的getattr,源码: #name=我们要的属性,比如request.form的form,name=form def __getattr__(self, name): if name == "__members__": return dir(self._get_current_object()) #name-->form,self._get_current_object()===>ctx.request,form return getattr(self._get_current_object(), name) 最后我们发现self._get_current_object()是我们在实例化 LocalProxy(partial(_lookup_req_object, "request")) 传过来的偏函数执行结果。 3.1.1:self._get_current_object()要执行的代码是: def _get_current_object(self): if not hasattr(self.__local, "__release_local__"): return self.__local() try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError("no object bound to %s" % self.__name__) 3.1.1.1 self._get_current_object()这个里面的self.__local(): 我们发先这个self.__local是在实例化的时候传过来: def __init__(self, local, name=None): object.__setattr__(self, "_LocalProxy__local", local) 我们发现传过来的是partial(_lookup_req_object, "request")偏函数: 偏函数源码: #name=request def _lookup_req_object(name): top = _request_ctx_stack.top #top就是我们前面放进去ctx if top is None: raise RuntimeError(_request_ctx_err_msg) #getattr(top, name)的意思就是到ctx里面找request return getattr(top, name) 3.1.1.1.1 top = _request_ctx_stack.top: # _request_ctx_stack是 LocalStack()的对象: 源码: @property def top(self): try: #返回ctx return self._local.stack[-1] except (AttributeError, IndexError): return None
总结:其实操作flask的请求上下文就是操作Local中的字典__storage__ 1.通过RequestContext类首先实例化ctx请求上下文对象,其内部包含请求对象 2.入栈,通过请求上下文对象的类的push()方法触发了LocalStack类的push() 方法,从而添加到Local类中的字典里。 3.观察导入的request源码 ,通过观察LocalProxy的源码,最后触发了LocalStack的top()方得到上下文对象,再得到请求对象, 从而实现reuqest的功能。到请求对象,从而实现reuqest的功能。 4.出栈通过请求上下文对象的类的方法,触发了LocalStack的的pop()方法从而从字典中删除掉当前线程或当前携程的请求信息。