Flask请求上下文
Flask请求上下文
# 说实话,当时看完视频知道怎么回事。就是容易忘..... # 不过总不能一直看视频对吧,尽快理解和掌握吧....惭愧啊
预备知识
1. werkzeug
当我们下载flask模块时该模块也会被下载,那么该模块有什么用处呢? 我们看下面的代码
from werkzeug.wrappers import Response, Request from werkzeug.serving import run_simple @Request.application def app(req): print(req) print(req.method) print(req.path) return Response("200 OK!") run_simple(hostname="127.0.0.1", port=8800, application=app)
-
看运行结果
-
是不是很好奇?它怎么会有request对象
这些我们暂且不讨论,当我们运行run_simple时,是不是相当于执行了app函数 ---> app() (可能你不知道现在在干什么,往下看)
2. 线程安全
.....
3. partial
-
偏函数
-
示例
from functools import partial def ab(a, b): print(a) return a + b # 第一个参数为对象,后面可以指定参数: a=1, b=2 new_ab = partial(ab, a = "request", b="200 OK!") new_ab = partial(ab, "request") # 指定后就不需要再传参了 # print(new_ab()) print(new_ab("200 OK!"))
请求上下文
1. 请求上文
-
flask实例
from flask import Flask, request app = Flask(__name__) @app.route("/") def index(): return "123" if __name__ == '__main__': app.run() # 进去看run源码 -
run方法
.....省略..... from werkzeug.serving import run_simple # 熟悉吧,就刚刚的 # 我们就可以继续引申我们的flask的app实例运行时,也可以说是这样执行了app实例:通过对象()方式执行 try: # flask实例也是通过run_simple运行起来的,也就是说执行时是 flask实例+()执行 # 但实例+()怎么执行呢? __call__方法啊。想起来了不 run_simple(host, port, self, **options) finally: self._got_first_request = False -
也就是说,flask实例执行__call__方法 from flask import Flask, request app = Flask(__name__) @app.route("/") def index(): return "123" if __name__ == '__main__': app.run() # 进去看run源码 app.__call__ # 继续进去看源码 -
call方法
def __call__(self, environ, start_response): # self = app = Flask(), environ为请求原始信息 """The WSGI server calls the Flask application object as the WSGI application. This calls :meth:`wsgi_app` which can be wrapped to applying middleware.""" return self.wsgi_app(environ, start_response) -
wsgi_app方法
def wsgi_app(self, environ, start_response): self = app = Flask() # 最终返回一个RequestContext(),含有request、session属性(从environ --> 请求原始信息中取出的) ctx = self.request_context(environ) # ctx = RequestContext(..) ctx含有request、session属性 error = None try: try: ctx.push() # request_context.push() ..... 省略 .... -
继续看ctx.push()
def push(self): # self = ctx = RequestContext() = (request,session) top = _request_ctx_stack.top # 这里先直接过,是None # 不执行 if top is not None and top.preserved: top.pop(top._preserved_exc) ..... 省略 .... _request_ctx_stack.push(self) # self = ctx(request,session) -
看 request_ctx_stack.push(self),我们要先知道 request_ctx_stack
_request_ctx_stack = LocalStack() # 将该类进行实例化 def __init__(self): self._local = Local() # 继续实例化Local class Local(object): __slots__ = ("__storage__", "__ident_func__") def __init__(self): # 设置属性 object.__setattr__(self, "__storage__", {}) # get_ident是一个函数,是为了获取线程id,看预备知识 object.__setattr__(self, "__ident_func__", get_ident) # 线程id # Local = {"__storage__":{},"__ident_func__":get_ident} 是一个对象(就是为了看起来方便,不是一个字典) -
最终也就是说
_request_ctx_stack = LocalStack() # LocalStack()该实例化属性含有_local属性 # _local = {"__storage__":{},"__ident_func__":get_ident} _request_ctx_stack = LocalStack() --> # ._local >>> {"__storage__":{},"__ident_func__":get_ident} -
继续回到_request_ctx_stack.push(self),看push方法
def push(self, obj): # obj = ctx = (request,session), self = _request_ctx_stack = LocalStack() # _local >>> {"__storage__":{},"__ident_func__":get_ident} rv = getattr(self._local, "stack", None) if rv is None: self._local.stack = rv = [] # .stack = xx,没有该属性,执行setattr方法 # 可能涉及拷贝,对rv操作就对字典中的[]也进行了操作 rv.append(obj) # {"__storage__":{9527:{stack:[ctx(request,session)]}},"__ident_func__":get_ident} return rv # {9527:{"stack":[ctx(request,session)]}} def __setattr__(self, name, value): # name = stack, value = [] ident = self.__ident_func__() # 获取线程id,假设为9527 storage = self.__storage__ # 获取到一个 空{} try: storage[ident][name] = value # storage = {ident:} except KeyError: storage[ident] = {name: value} # storage = {9527:{"stack:[]}}
-
2. 请求下文
-
需要用到上面的
partial
-
flask实例
from flask import Flask, request app = Flask(__name__) @app.route("/") def index(): print(request) # 点进去看源码 print(request.method) return "123" if __name__ == '__main__': app.run() # 进去看run源码 -
request:
request = LocalProxy(partial(_lookup_req_object, "request")) # 将request作为参数传入 -
_lookup_req_object
def _lookup_req_object(name): # name = request # 这里省了一点步骤,你可以继续点进top,记得有时候需要getattr或者setattr top = _request_ctx_stack.top # _request_ctx_stack = [ctx(request,session)] # top = ctx(request,session) = RequestContext(),里面含有request、session等属性 if top is None: raise RuntimeError(_request_ctx_err_msg) # 从ctx获取request属性 return getattr(top, name) -
该偏函数执行后返回一个request对象(该对象就是request自身)
-
-
从request获取属性,例如method
request.method # 没有该属性,执行LocalProxy中的__gatattr__方法,先进行实例化再执行__gatattr__方法 request = LocalProxy(partial(_lookup_req_object, "request")) # 将request作为参数传入 -
实例化
def __init__(self, local, name=None): # local就是偏函数partial,偏函数执行可以获取request真身 object.__setattr__(self, "_LocalProxy__local", local) object.__setattr__(self, "__name__", name) # 判断该偏函数是否可以执行和是否不含有__release_local__属性,成立!!! if callable(local) and not hasattr(local, "__release_local__"): object.__setattr__(self, "__wrapped__", local) # 设置属性 -
执行__gatattr__方法
def __getattr__(self, name): # name = method, form ... if name == "__members__": return dir(self._get_current_object()) # 从某个对象中执行getattr方法,执行self._get_current_object()方法获取对象 return getattr(self._get_current_object(), name) -
执行_get_current_object()方法
def _get_current_object(self): # 看实例化:object.__setattr__(self, "_LocalProxy__local", local),设置类的私有属性值 if not hasattr(self.__local, "__release_local__"): # 获取私有属性,该私有属性值为local,也就是偏函数 return self.__local() # 执行偏函数,获取request真身 try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError("no object bound to %s" % self.__name__) -
结束:
def __getattr__(self, name): # name = method, form ... if name == "__members__": return dir(self._get_current_object()) # 从某个对象中执行getattr方法,执行self._get_current_object()方法获取对象 return getattr(self._get_current_object(), name) # 从request中获取method等属性
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix