美文网首页
【OK】flask笔记(十五):上下文对象

【OK】flask笔记(十五):上下文对象

作者: warmsirius | 来源:发表于2019-09-25 08:02 被阅读0次

    一、Flask如何接收客户端发送请求?

    python框架程序从客户端收到请求时,要让视图函数能访问一些对象,且该对象需要封装HTTP请求,这样才能处理客户端请求。如一般所说的请求对象,就是封装了客户端发送的HTTP请求。

    在Django框架中,让视图函数能访问请求对象,使用的是显而易见的方式:将封装HTTP对象的request作为参数传入视图函数,所以 Django中的每个视图函数都增加了一个参数request。(P.S.:在Django中session也被封装到了request中)

    为了避免大量可有可无的参数把视图函数弄得复杂,Flask使用上下文临时把某些对象变为全局可访问。

    二、如何理解Flask上下文中“全局”?

    • 带有全局变量request的视图函数
    from flask import request, Flask
    
    app = Flask(__name__)
    
    
    @app.route("/")
    def index():
        user_agent = request.headers.get("User-Agent")
        return "<p>你的浏览器是: {}</p>".format(user_agent)
    

    Q: 在上面的视图中,我们把request当做全局变量来使用,试想一下,request真的是全局变量吗?

    试想在多线程服务器中,多个线程同时处理不同客户端发送的不同请求时,每个线程看见的request对象必然不同。如果Flask中的request是所有线程全局可访问,那么在多线程中就会出现A可以访问B的请求对象,这样会存在数据安全隐患,多个用户请求也会出现混乱,而且试想N个人登录服务器,request如果是真正的全局,那么服务器也不可能承受request变量这么多封装的数据。

    存在的问题

    Re: Flask上下文的全局指让 Flask使用上下文让特定的变量在一个线程中全局可访问,与此同时却不会干扰其他线程。所以 Flask全局==线程全局

    线程全局

    提示:

    • 线程是可单独管理的最小指令集。
    • 进程通常使用多个活动线程,有时还有共享内存或文件句柄等资源。
    • 多线程Web服务器会创建一个线程池,再从线程池中选择一个线程用于处理接收到的请求。

    三、Flask上下文全局变量

    Flask中有两种上下文:程序上下文和请求上下文。

    变量名 上下文 说明
    current_app 程序上下文 当前激活程序的程序实例
    g 程序上下文 处理请求时用作临时存储的对象。每次请求都会重设这个变量
    request 请求上下文 请求对象,封装了客户端发出的HTTP请求中的内容
    session 请求上下文 用户会话,用于存储请求之间需要“记住”的值的词典

    注意: g和请求没关系,可以在请求之间传递参数,每次进入请求之前,g都是清空的,请求完成之后,g又被清空了。 临时存储的对象,每次请求都会重设这个变量。

    Flask在分发请求之前激活(或推送)程序和请求上下文,请求处理完成后再将其删除。

    • 程序上下文被推送后,就可以在线程中使用 current_app 和 g 变量
    • 请求上下文被推送后,就可以使用 request 和 session 变量

    注意:如果使用这些变量时我们没有激活程序上下文或请求上下文,就会报错。

    from flask import request, Flask, current_app
    
    app = Flask(__name__)
    
    
    @app.route("/")
    def index():
        user_agent = request.headers.get("User-Agent")
        return "<p>你的浏览器是: {}</p>".format(user_agent)
    
    
    if __name__ == "__main__":
        print(current_app.name)
    

    报错如下:

    Traceback (most recent call last):
      File "/Users/yuanjun/PycharmProject/code/demos/demo.py", line 25, in <module>
        print(current_app.name)
      File "/Users/yuanjun/.virtualenvs/flask_py3/lib/python3.6/site-packages/werkzeug/local.py", line 348, in __getattr__
        return getattr(self._get_current_object(), name)
      File "/Users/yuanjun/.virtualenvs/flask_py3/lib/python3.6/site-packages/werkzeug/local.py", line 307, in _get_current_object
        return self.__local()
      File "/Users/yuanjun/.virtualenvs/flask_py3/lib/python3.6/site-packages/flask/globals.py", line 52, in _find_app
        raise RuntimeError(_app_ctx_err_msg)
    RuntimeError: Working outside of application context.
    
    This typically means that you attempted to use functionality that needed
    to interface with the current application object in some way. To solve
    this, set up an application context with app.app_context().  See the
    documentation for more information.
    

    在这个例子中,没激活程序上下文之前就调用currrent_app.name会导致错误,提示当前运行没有application context。

    解决办法:利用app.app_context()获得一个程序上下文,然后push到当前的app中。

    if __name__ == "__main__":
        app_ctx = app.app_context()
        print("app_ctx.__dict__:", app_ctx.__dict__)
        app_ctx.push() # 手动入栈
    
        print("current_app:", current_app)
        print("current_app.name:", current_app.name)
    
        print("app:", app)
        print("app.name:", app.name)
        app_ctx.pop()
    

    输出如下:

    app_ctx.__dict__: {'app': <Flask 'demo'>, 'url_adapter': None, 'g': <flask.ctx._AppCtxGlobals object at 0x10fab8eb8>, '_refcnt': 0}
    
    current_app: <Flask 'demo'>  # 得到的是Flask应用实例
    current_app.name: demo
    
    app: <Flask 'demo'>   # 得到的是Flask应用实例
    app.name: demo
    

    同样的请求上下文也有一个 app.request_context(),关于这两个知识点,推荐文章如下:

    相关文章

      网友评论

          本文标题:【OK】flask笔记(十五):上下文对象

          本文链接:https://www.haomeiwen.com/subject/axdryctx.html