在flask的ctx.py文件中
可以看到AppConext类和RequestContext类
在flask的global.py中,可以看到current_app、request、session和g都是LocalProxy对象
# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app) #_find_app是找到app核心对象
request = LocalProxy(partial(_lookup_req_object, 'request')) #这里是找到request核心对象
session = LocalProxy(partial(_lookup_req_object, 'session'))
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 变量。如果使用这些变量时没有激活应用上
下文或请求上下文,就会导致错误。
请求上下文
request
通过request 请求的属性或方法
应用上下文
是对Flask的封装
current_app
app代表的是app = Flask(name)的app
应用场景
例如在蓝图py中,需要获取app.config[‘xx’]的信息,那么操作方式可能是from main import app,但是主程序中也会导入蓝图文件,那么就会存在循环导入的问题,因此,使用全局变量current_app则可以解决这个问题。
例如app.py
from flask import Flask
def create_app(Config):
app = Flask(__name__)
app.config.from_object(Config)
return app
class DeConfig():
SECRET_KEY = 'xxx000999'
app = create_app(DeConfig)
from bl import my_bl
app.register_blueprint(my_bl, url_prefix='/bl/')
if __name__ == '__main__':
app.run()
/bl.init.py
from flask import Blueprint, current_app
my_bl = Blueprint(__name__, 'my_bl')
@my_bl.route('/hi')
def hi():
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帮助调试
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
with app.app_context():
print(current_app.xxx)
设置变量
读取变量
g.var_name
案例
认证机制
对于特定视图,可以提供强制要求用户登录的限制;【涉及到装饰器】
对于所有视图,无论是否强制要求用户登录,都可以在视图中尝试获取用户认证后的身份信息。【涉及到钩子】
路径:请求 — 请求钩子(尝试判断用户身份,对未登录用户不做处理,放行)用g对象保存用户身份信息
— 普通试图处理 /(强制登录视图—装饰器)
这里涉及到了两个装饰器,一个是路由装饰器,一个是视图函数的强制登录的装饰器,因为强制登陆的装饰器是要增加get_user_profile视图函数的功能,因此路由装饰器要在其外面。
from flask import Flask, abort, current_app, g
app = Flask(__name__)
# 请求钩子(尝试判断用户身份,对未登录用户放行) 使用g对象保存用户信息
@app.before_request
def authentication():
"""
利用before_request请求钩子,在进入所有视图前先尝试判断用户身份
"""
# 可以使用cookie、session、jwt等鉴权
# 如果用户已登陆,用户则有身份信息
# g.user_id = '123'
# 如果用户未登录,用户无身份信息
g.user_id = None
# 强制登陆的装饰器
def login_required(func):
def wrapper(*args, **kwargs):
# 判断用户是否登陆了
if g.user_id:
return func(*args, **kwargs)
else:
abort(401)
return wrapper
@app.route('/')
def index():
return "Hello {}".format(g.user_id)
@app.route('/profile')
@login_required
def get_user_profile():
return "user profile page user_id = {}".format(g.user_id)
if __name__ == '__main__':
app.run(port=8000, debug=True)
如果user_id为None的话,此时访问http://127.0.0.1:8000/profile,结果为