Cou*_*cha 4 python api config key flask
如果 api-key 在请求标头内无效(或未指定),我已经为我的路由函数设置了一个装饰器来中止(401)。
因此,我在主模块(名为 app)的专用文件中定义了此函数,并将其导入到我的所有蓝图定义中。但从这个文件中,我无法访问应用程序对象来加载存储在 config.json 中的 API 密钥。我尝试使用 current_app,但出现错误告诉我无法在应用程序上下文之外使用它。我无法直接从应用程序模块导入应用程序对象,因为它会导致循环导入。
如果我在文件内声明装饰器__init__.py,我将面临同样的问题:我将无法从应用程序模块导入 require_apikey 函数,因为蓝图模块已在应用程序模块中导入以进行蓝图注册。
我认为我的设计存在一些问题。你能指出我的缺陷并帮助我改正吗?
我的项目目录如下所示:
project/
- run.py
- app/
- - __init__.py
- - player.py
- - require_apikey.py
Run Code Online (Sandbox Code Playgroud)
以下是这些文件的内容:
# run.py
from app import app
if __name__ == "__main__":
app.run()
Run Code Online (Sandbox Code Playgroud)
# app/__init__.py
from flask import (
Flask,
render_template,
jsonify)
from app.db import db
from app.player import player
app = Flask(__name__)
app.config.from_object("app.config.Config")
app.register_blueprint(player, url_prefix="/player/")
db.init_app(app)
with app.app_context():
db.create_all()
Run Code Online (Sandbox Code Playgroud)
# app/player.py
from flask import (
Blueprint,
request,
abort,
jsonify)
from app import require_apikey
from app.models.player import Player
from app.db import db
player = Blueprint(__name__, __name__)
@player.route('/', methods=["GET", "POST"])
@require_apikey
def root():
# Do some stuff
Run Code Online (Sandbox Code Playgroud)
# app/require_apikey.py
from functools import wraps
from flask import (
request,
abort,
current_app
)
API_KEY = current_app.config["SECRET_KEY"]
def require_apikey(view_function):
@wraps(view_function)
def decorated_function(*args, **kwargs):
if request.headers.get("api-key") and request.headers.get("api-key") == API_KEY:
return view_function(*args, **kwargs)
else:
abort(401, "Invalid API key")
return decorated_function
Run Code Online (Sandbox Code Playgroud)
app在项目文件中初始化__init__非常简单,但当项目规模变大时也非常有限(并且由于您正在为路线使用蓝图,我猜您的项目规模已经足够大了)。
在这种情况下,推荐的初始化方法app是使用App 工厂,它基本上是一个创建并返回实例的函数app。
这是一个工作树栖的简单示例(可能不是您会找到的最好的示例,但应该可以):
# myapp/application/setup.py
from flask import Flask
from .application.extensions import db
def create_app():
app = Flask(__name__)
app.config.from_object("myapp.config.Config")
# Initialize extensions
db.init_app(app)
with app.app_context():
db.create_all()
# Register Blueprints
from myapp.player import player
app.register_blueprint(player, url_prefix="/player/")
return app
Run Code Online (Sandbox Code Playgroud)
# myapp/application/extensions.py
from flask_sqlalchemy import SQLAlchemy
# define global extensions in a separate file so that they can be imported from
# anywhere else in the code without creating circular imports
# the proper initialization is made within the `create_app` function
db = SQLAlchemy()
Run Code Online (Sandbox Code Playgroud)
# myapp/application/app.py
from .setup import create_app
app = create_app()
Run Code Online (Sandbox Code Playgroud)
# myapp/__init__.py
from .application.app import app
Run Code Online (Sandbox Code Playgroud)
# run.py
from myapp import app
if __name__ == "__main__":
app.run()
Run Code Online (Sandbox Code Playgroud)
这就是您项目的层次结构。此时,您已经myapp/application/app.py初始化了app变量,并且可以从任何您想要的地方导入,而不必担心导入循环。
根据我建议的树木,并考虑到您相应地更新导入,您的装饰器现在应该按预期工作。但是,如果我告诉您 Flask 提供了一种无需实现装饰器即可完成您想要的操作的方法,该怎么办?这就是发挥作用的地方before_request。这是一个您可以编写的特殊函数,将在应用程序上的每个请求之前在应用程序上下文中调用。
from myapp.application.app import app
@app.before_request
def require_apikey():
if request.headers.get("api-key") != API_KEY:
abort(401, "Invalid API key")
Run Code Online (Sandbox Code Playgroud)
现在的问题是,将为您定义的每个端点调用此函数,也许这不是您想要的。但不用担心,您还可以定义一个before_request函数来附加到特定的蓝图。
# myapp/my_blueprint.py
from myapp.tools import require_apikey
my_blueprint = Blueprint(...)
my_blueprint.before_request = require_apikey
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3221 次 |
| 最近记录: |