Flask限流问题
我在网站上尝试对API请求进行速率限制。然而,当我使用Flask-Limiter时,它却被忽略了。
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
这是我定义limiter的位置:
limiter = Limiter(
key_func=get_remote_address,
default_limits=[],
storage_uri=os.environ.get("REDIS_URL"),
)
我的用法:
@app.route('/api/gen')
@limiter.limit("1 per day")
def link():
link=genlink()
if link:
return link
else:
return jsonify({"error": "No link available"}), 404
我已经正确输入了Redis的 URL,且自部署以来这是我第一次使用Redis,而不是本地环境。这个网页应用托管在Vercel。
解决方案
这是第一个问题:你原本的 Limiter(...) 调用从未绑定应用:
Python
limiter = Limiter(
key_func=get_remote_address,
default_limits=[],
storage_uri=os.environ.get("REDIS_URL"),
)
所有遵循“应用工厂”设计的Flask扩展在创建扩展对象后,只有在将app=app作为参数传给构造函数,或在创建扩展对象后调用limiter.init_app(app) 时,才需要Flask应用对象。若两者都缺失,Limiter将创建一个未绑定的对象;虽然 @limiter.limit(...) 装饰了规则,但它并未接入请求生命周期,因此不会强制执行任何操作,也不会报错。这正是你所经历的行为:没有错误,只是被忽略的调用。官方快速入门指南也能观察到相同的行为:limiter = Limiter( get_remote_address, app=app, storage_uri="redis://localhost:6379", storage_options={"socket_connect_timeout": 30}, strategy="fixed-window" ) 参见: https://flask-limiter.readthedocs.io/en/stable/
第二个问题
当你尝试添加app=app时在本地能工作,但在部署时却报错,这很可能是文件/导入顺序的问题,而不是Redis的问题。这是一个因为在该命名空间中执行app = Flask(_name_) 之前就创建了Limiter(app=app, ...) 的问题,在Vercel上很常见,因为无服务器入口点(通常api/index.py)会从另一个文件导入你的应用,而Limiter(app=app) 在这种情况下所在的模块在你的应用甚至存在之前就被导入了。
解决方案:通过init_app() 实现初始化与绑定的解耦
我在这里附上应该可用的代码
import os
from flask import Flask, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
# Construct without app= to avoid ordering/circular-import issues
limiter = Limiter(
key_func=get_remote_address,
default_limits=[],
storage_uri=os.environ.get("REDIS_URL"),
storage_options={"socket_connect_timeout": 30},
)
limiter.init_app(app) # bind AFTER app exists — this is the critical step
def genlink():
...
@app.route('/api/gen')
@limiter.limit("1 per day")
def link():
result = genlink()
if result:
return result
return jsonify({"error": "No link available"}), 404