如何让Express应用按独立用户速率调用Anilist API而非服务器统一速率?
我有一个部署在Render免费服务上的Express后端应用,为前端提供API服务。该应用通过anilist-node调用Anilist第三方API获取数据,Anilist的非商用速率限制为每个用户每分钟90次请求。
我发现当前所有前端用户的请求都会被Anilist视为来自Express服务器这单一用户(因服务器IP/域名固定),导致多用户共享90次/分的请求额度:若10人使用,每人仅能分到9次/分;若90人使用,每人仅1次/分。
请帮我确认这个理解是否正确,如果正确,请问如何让应用按独立用户的速率调用API,即每个用户独立享有90次/分的请求额度?
以下是我的Express应用结构与核心代码:
目录结构
. └── Express root folder/ └── src/ ├── model.js ├── routes.js ├── controller.js └── index.js
index.js
const express = require("express") const cors = require("cors") const path = require("path") const routes = require("./routes") const app = express() const PORT = process.env.PORT || 3000 app.use(cors()) app.set("trust proxy", true) app.use((req, res, next) => { res.setHeader("Access-Control-Allow-Origin", "*") res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ) next() }) app.use("/api", routes) app.listen(PORT, () => { console.log("Server is running", PORT) })
routes.js
const express = require("express") const controller = require("./controller") const router = express.Router() router.get("/", controller.getData) module.exports = router
controller.js
const Model = require("./model") class Controller { static async getData(req, res, next) { try { const data = await Model.getData() res.json({ success: true, data }) } catch (err) { res .status(404) .send({ success: false, data: "Fail and get 404" }) } } } module.exports = Controller
model.js
const anilist = require("anilist-node") const Anilist = new anilist() // 我用来获取数据的扩展 class Model { static async getData() { // 这里调用Anilist扩展的操作,会占用服务器的请求额度,而非用户的 return data } } module.exports = Model
你的理解完全正确
Anilist的速率限制是基于请求发起方的身份标识(通常是IP地址,若用户有认证则是用户令牌)。当前你的后端作为统一代理发起请求,所有请求都来自服务器IP,自然会被Anilist判定为同一个用户,共享90次/分的额度。
实现每个用户独立享有速率额度的方案
要让每个前端用户独立占用Anilist的速率额度,核心思路是让请求直接关联用户身份,而非服务器身份。以下是两种可行方案:
方案1:前端直接调用Anilist API(推荐)
让前端应用直接集成anilist-node或通过GraphQL请求Anilist API,这样每个请求都来自用户的浏览器IP,Anilist会按每个用户的IP独立计算速率限制。
- 优势:完全绕过后端的代理瓶颈,后端只需提供业务逻辑,无需承担API请求的速率压力。
- 注意事项:需在前端处理Anilist的认证(若需访问用户私有数据)、错误处理及速率限制提示逻辑。
方案2:后端转发用户身份标识(适合需后端处理数据的场景)
如果必须通过后端转发请求,可以让前端用户提供自己的Anilist访问令牌,后端调用API时携带该令牌:
- 前端用户通过Anilist的OAuth流程获取访问令牌,并在请求后端API时将令牌放在请求头(如
Authorization: Bearer <user-token>)中。 - 修改后端代码,传入用户令牌初始化Anilist客户端:
// model.js 修改后的代码 const anilist = require("anilist-node") class Model { static async getData(userToken) { // 使用用户的令牌初始化Anilist客户端 const Anilist = new anilist(userToken) // 执行API请求,此时Anilist会按该用户的令牌计算速率限制 const data = await Anilist.media.search("xxx") return data } } module.exports = Model - 在controller中获取并传递令牌:
// controller.js 修改后的代码 const Model = require("./model") class Controller { static async getData(req, res, next) { try { const userToken = req.headers.authorization?.split("Bearer ")[1] if (!userToken) { return res.status(401).send({ success: false, data: "Missing authorization token" }) } const data = await Model.getData(userToken) res.json({ success: true, data }) } catch (err) { res .status(404) .send({ success: false, data: "Fail and get 404" }) } } } module.exports = Controller
- 注意事项:需确保后端妥善处理用户令牌,避免泄露;同时要处理令牌过期等异常情况。
补充:后端层面的用户限流(可选)
如果不想让前端直接请求Anilist,可在后端对每个用户(按IP或用户ID)单独限流,确保每个用户请求频率不超90次/分,再统一转发给Anilist。但这种方式仅能限制后端接收的请求量,Anilist仍按服务器IP计算总额度,仅适合用户量不大的场景。可使用express-rate-limit包实现:
// index.js 中添加限流配置 const rateLimit = require("express-rate-limit") const userLimiter = rateLimit({ windowMs: 60 * 1000, // 1分钟 max: 90, // 每个用户最多90次请求 keyGenerator: (req) => req.ip // 按用户IP标识用户 }) app.use("/api", userLimiter, routes)
内容的提问来源于stack exchange,提问作者Bunny

