在flask的ctx.py文件中
可以看到AppConext类和RequestContext类
在flask的global.py中,可以看到current_app、request、session和g都是LocalProxy对象

  1. # context locals
  2. _request_ctx_stack = LocalStack()
  3. _app_ctx_stack = LocalStack()
  4. current_app = LocalProxy(_find_app) #_find_app是找到app核心对象
  5. request = LocalProxy(partial(_lookup_req_object, 'request')) #这里是找到request核心对象
  6. session = LocalProxy(partial(_lookup_req_object, 'session'))
  7. g = LocalProxy(partial(_lookup_app_object, 'g'))

原理

Threadlocal线程局部变量

例如request虽然是全局变量,但是不同的请求的request.args.get(‘field_name’)获取到的值是不同的。
/name?name=Tom
/name?name=Jack

request.args={
‘thread_a_name’:’Tom’,
‘thread_a_name’:’Jack’
}

因此,单独上下文时,需要使用with app.app_context和with app.request_context

都是当作全局变量使用,实际上是线程内部的全局变量。

上下文代表的是环境,即不同的线程。例如request虽然是全局变量,但是在不同的场景下是不一样的。

Flask 在分派请求之前激活(或推送)应用和请求上下文,请求处理完成后再将其删除。应
用上下文被推送后,就可以在当前线程中使用 current_app 和 g 变量。类似地,请求上下
文被推送后,就可以使用 request 和 session 变量。如果使用这些变量时没有激活应用上
下文或请求上下文,就会导致错误。

image.png

请求上下文

是对Request对象的封装

request

通过request 请求的属性或方法

应用上下文

是对Flask的封装

并不是一直存在的

current_app

app代表的是app = Flask(name)的app

应用场景

例如在蓝图py中,需要获取app.config[‘xx’]的信息,那么操作方式可能是from main import app,但是主程序中也会导入蓝图文件,那么就会存在循环导入的问题,因此,使用全局变量current_app则可以解决这个问题。
例如app.py

  1. from flask import Flask
  2. def create_app(Config):
  3. app = Flask(__name__)
  4. app.config.from_object(Config)
  5. return app
  6. class DeConfig():
  7. SECRET_KEY = 'xxx000999'
  8. app = create_app(DeConfig)
  9. from bl import my_bl
  10. app.register_blueprint(my_bl, url_prefix='/bl/')
  11. if __name__ == '__main__':
  12. app.run()

/bl.init.py

  1. from flask import Blueprint, current_app
  2. my_bl = Blueprint(__name__, 'my_bl')
  3. @my_bl.route('/hi')
  4. def hi():
  5. return current_app.config['SECRET_KEY']

此时访问http://127.0.0.1:5000/bl/hi时,可以在页面上看到SECRET_KEY的值。

g

使用場景

在视图函数操作中,可能会调用到其他的函数,并且会传递参数。一个参数还好,但是参数一多就比较麻烦了。使用g可以解决这个问题。g可以作为一个容器存储相关的变量信息。

g起到的是临时存储的作用,每次请求后g都会被清空。

app_context

正常情况下无法直接调试时使用current_app和g,可以使用app_context帮助调试

  1. from flask import Flask
  2. app = Flask(__name__)
  3. @app.route('/')
  4. def index():
  5. return '<h1>Hello World!</h1>'
  6. with app.app_context():
  7. print(current_app.xxx)

设置变量

g.var_name = ‘xxx’

读取变量

g.var_name

案例

认证机制

对于特定视图,可以提供强制要求用户登录的限制;【涉及到装饰器】
对于所有视图,无论是否强制要求用户登录,都可以在视图中尝试获取用户认证后的身份信息。【涉及到钩子】

路径:请求 — 请求钩子(尝试判断用户身份,对未登录用户不做处理,放行)用g对象保存用户身份信息
— 普通试图处理 /(强制登录视图—装饰器)

这里涉及到了两个装饰器,一个是路由装饰器,一个是视图函数的强制登录的装饰器,因为强制登陆的装饰器是要增加get_user_profile视图函数的功能,因此路由装饰器要在其外面。

  1. from flask import Flask, abort, current_app, g
  2. app = Flask(__name__)
  3. # 请求钩子(尝试判断用户身份,对未登录用户放行) 使用g对象保存用户信息
  4. @app.before_request
  5. def authentication():
  6. """
  7. 利用before_request请求钩子,在进入所有视图前先尝试判断用户身份
  8. """
  9. # 可以使用cookie、session、jwt等鉴权
  10. # 如果用户已登陆,用户则有身份信息
  11. # g.user_id = '123'
  12. # 如果用户未登录,用户无身份信息
  13. g.user_id = None
  14. # 强制登陆的装饰器
  15. def login_required(func):
  16. def wrapper(*args, **kwargs):
  17. # 判断用户是否登陆了
  18. if g.user_id:
  19. return func(*args, **kwargs)
  20. else:
  21. abort(401)
  22. return wrapper
  23. @app.route('/')
  24. def index():
  25. return "Hello {}".format(g.user_id)
  26. @app.route('/profile')
  27. @login_required
  28. def get_user_profile():
  29. return "user profile page user_id = {}".format(g.user_id)
  30. if __name__ == '__main__':
  31. app.run(port=8000, debug=True)

如果user_id为None的话,此时访问http://127.0.0.1:8000/profile,结果为
image.png