flask的请求到来阶段
项目初始化阶段
源码:
from flask import Flask,request,render_template # 第一步:app = Flask(__name__,static_folder="static") # 第二步:app.config.from_object("xxx.xxx") # 第三步: @app.before_request def f1(): print('before') @app.after_request def f2(): print('after') @app.before_first_request def f3(): print('before first') # 第四步: @app.route('/index') def index(): print('123') return "456" # 第五步: if __name__ == '__main__': app.run( host='127.0.0.1', port='12122' )
源码分析
过程: 1.当程序走到第一步的时候,会实力化一个Flask()对象,赋值给app,执行Flask()类的__init__方法 作用: 其中__init__主要做的事情就是对对象变量的初始化和类变量的初始化,以及对对象变量的赋值操作 对象变量包括: def __init__( self, static_url_path=None, static_folder='static', template_folder='templates', instance_path=None, root_path=None ) 类变量包括: request_class = Request response_class = Response config_class = Config url_rule_class = Rule session_interface = SecureCookieSessionInterface() template_folder = None 对对象的赋值操操作: self.config = self.make_config(instance_relative_config) self.view_functions = { endponit:函数名 } self.before_request_funcs = { None:[f1,f2] } self.after_request_funcs = { None:[f1,f2] } self.before_first_request_func = [] self.url_map = Map() 2.第二步: 作用: 读取所有的配置文件中的每一项配置,将其赋值在app对象的config的属性中 其中app.config调用的是make_config函数,make_config函数调用config_class,继承的是Config类,Config继承的是dict的字典类型 具体负责读取每一项配置的时from_object函数,将其赋值给app的.config对象 3.第三步: 作用:在视图函数执行之前,添加一些装饰器方法。 其中@app.before_request调用了before_request_funcs变量,将所有的befor装饰的函数名以字典的方式存放在before_request_funcs里面格式如下 before_request_funcs= { None:[f1,f2] } 其中@app.after_request调用了after_request_funcs变量,将所有的after装饰的函数名以字典的方式存放在after_request_funcs里面格式如下 after_request_funcs= { None:[f3,f4] } 其中@app.before_first_request调用了before_first_request_funcs,将所有的before_first_request装饰的函数名以列表的方式存放在before_first_request里面格式如下 before_first_request =[ f5,f6 ] 4.第四步: 作用:对请求路由进行封装,存在app对象的url_map中 https://www.cnblogs.com/p0st/p/11913399.html 5.第五步: 作用:启动werkzung,对请求进行监听 https://www.cnblogs.com/p0st/p/11908509.html
总结
其中:
flask里面初始化最重要的变量是下面8个
url_map:map对象-->rule对象-->url,endponit,methods
view_functions:endponit <---> 视图函数
config:读取的所有配文件的配置
before_request:视图函数开始执行前要执行的函数
after_request:视图函数执行之后要执行的函数
before_first_request:只执行一次的函数,在before_request前执行
还有两个localstack对象:
AppContext
RequestContext
请求到来阶段
注意
environ:就是浏览器发送过来数据,都是原始的值,字符串,相当于request.meta。是请求的原始数据,
经过封装之后,在调用request.methods request.data,cookie,user-agent,host等用户请求相关的所有信息
start_response 是 application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息。
源码
其中app.__call__执行了wsgi_app,里面做了大概三件事情 ctx = self.request_context(environ) error = None try: try: ctx.push() 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)
过程
1.视图函数执行之前(请求到来时的准备阶段): 1.1 ctx = self.request_context(environ) 1.1.1 执行了ctx = self.request_context(environ),将请求的原始数据传递进去,并返回了一个RequestContext的对象 1.1.2 在RequestContext其实做的就是对请求对象的封装,在判断request为空之后,执行request = app.request_class(environ) 1.1.3 在项目启动阶段的时候,我们知道类变量中request_class是Request的类。点进去看到Request继承了RequestBase 1.1.4 点进去BaseRequest我们可以看到好多request的方法,cookie,args,form等 1.1.5 在对request封装好之后,在RequestContext赋值给self.request = request self.session = session =None 总结: 所以wsgi_app里面的ctx就等于封装好的request对象和空的session,变成RequestContext对象 request = Request(environ) session = None 所以 ctx=RequestContext() 1.2 执行ctx.push() # 执行的是ctx,也就是RequestContext对象的push方法 1.2.1 执行app_ctx = self.app.app_context(),创建AppContxt对象(app,g) =app-ctx,并将app-ctx存放到LocalStack()---->AppContxt对象 1.2.2 执行_request_ctx_stack.push(self),其中self等于的是调用该push的对象,也就是ctx=RequestContext(),也放到了Localstack()---->RequestContext对象 1.2.3 执行self.session = session_interface.open_session(self.app, self.request)对空的session赋值()赵大 1.2.4 执行路由匹配 总结: 通过ctx对象生成两个Localstatck对象,其中一个是APPcontext(app,g)对象,一个是RequestContext(reuqest,session)对象 不知道对不对:appcontext对象是通过requestcontect对象创建出来的。所以在存值的时候会多两步创建的过程 2.寻找视图函数(视图函数执行阶段) 执行self.full_dispatch_request(),由于我们当前在Flask对象里面,所以要去Flask的full里面会自行dispathc_request方法 本质上就做三件事请: 1.执行before_frist_request和beofre_request 1.1.1 尝试的去执行self.try_trigger_before_first_request_functions()函数。 1.1.2 点进去,发现self.try_trigger_before_first_request_functions(),在做一个循环。 for func in self.before_first_request_funcs: func() 其中before_first_request_funcs就是我们在执行Flask__init__时候初始化的变量为空列表 也就是说before_first_request是先与视图函数执行的,并且before_first_request方法只有在 第一个请求进来时执行一次,可以通过_got_first_request来进行控制的是否执行 1.1.3继续执行preprocess_request(),是在执行请求之前调用的。作用是执行所有的beofre_request函数 注意: rv = self.preprocess_request() if rv is None: pass 通过代码我们也可以看出,只有在before_request没有返回值的时候,才会继续执行视图函数 进入preprocess_request函数,可以看到: funcs = self.before_request_funcs.get(None, ()), for func in funcs:。将我们在__init__里面的before_request_funcs取值,并循环执行,如果有返回值 直接返回给用户,剩下before_request和视图函数都不执行。(里面有一些处理蓝图的功能) 2.执行视图函数 2.1.1 执行 rv = self.dispatch_request(),是在执行视图函数。点击去 2.1.2 执行rule = req.url_rule,将url,methods和endpoint封装的rule对象找到。 2.1.3 执行return self.view_functions[rule.endpoint](**req.view_args),通过url和rule里面的 url进行对比,找到endpoint,在view_functions里面找到要执行的函数加()执行,并返回返回值 3.执行after_rrequest 3.1.1 执行self.finalize_request(rv)就是在执行after_request的全部函数 3.1.2 其中response = self.make_response(rv)将试图函数封装到response里面,进行返回 3.1.3 执行response = self.process_response(response) 3.1.3.1 操作蓝图里面的after_request以及执行其他的after-request函数 3.1.3.2 处理返回值的session,并加密放到cookie,返回给浏览器 总结: 1.在执行before和after等request的时候都需要处理和蓝图的关系,因为beforeh和after的request的作用域的问题,所以先执行蓝图里面的 2.before-request在执行之后不能有返回值,所以函数里面一定不能有return。因为返回了之后,就不会执行视图函数了 3.所有的视图函数执行完之后,如果有after_request的话,将返回值给after-request进行处理,并且session的处理也是在after-request部分 3.垃圾回收 主要是将locl中的appcontext和requestcontext的数据清除掉,避免内存泄漏 执行ctx.auto_pop(error),由于ctx等于的是self.request_context(environ)。方法,所以执行的pop是ctx的pop方法 在pop里面将local中的appcontext数据和requestcontext的数据都清除。
其他知识点
itertools里面的chain import itertools 将里面的两个列表打开,并依次取值,并不会生成新的列表。只是生成一个对象 v = itertools.chain([11,22,33],[55,66]) for item in v: print(item) functools里面的偏函数 import functools def test(a1,a2): print(a1,a2) new_test = functools.partial(test,'测试') new_test(2) 通过原函数生成一个新的函数,新函数的第一个位置是原函数的函数名,第二个位置是原函数的第一个参数 调用新函数时候,里面的函数是原函数的最后一个函数
了解流程之后,如何使用request,session,app,g等数据
1.request和session数据是如何获取的? 1.导入from Flask import globals 2.在globals最后可以看到一下代码 request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) 其中就是通过偏函数partial通过将_lookup_req_object变成新的函数LocalProxy来获取session和request对象 3.执行_lookup_req_object函数 def _lookup_req_object(name): top = _request_ctx_stack.top # 获取local对象中栈的最有一个ctx或者app_ctx对象 if top is None: raise RuntimeError(_request_ctx_err_msg) # 如果调用_lookup_req_object传进的参数是字符串类型的request return getattr(top, name) # 就通过反射取出request对象,并返回给视图函数
2.总结:
session,request,current_app,g都是LocalProxy的对象
通过源码得知:
执行session['x1'] = ctx.session['x1']
执行request.method = ctx.request.method
执行current_app.config = app_ctx.app.config
执行g.x1 = app_ctx.g.x1
3. g到底是个什么鬼?
在一次请求请求的周期,可以在g中设置值,在本次的请求周期中都可以读取或复制。相当于是一次请求周期的全局变量。
from flask import Flask,g
app = Flask(__name__,static_url_path='/xx')
@app.before_request
def f1():
g.x1 = 123
@app.route('/index')
def index():
print(g.x1)
return 'hello world'
if __name__ == '__main__':
app.run()
pass
上帝说要有光,于是便有了光;上帝说要有女人,于是便有了女人!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类