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()

复制代码

  flask源码流程图

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pass

posted @   thep0st  阅读(92)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· .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技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示