小程序与 H5 调用第三方 API:把密钥、域名和跨域一起收到服务端
小程序和 H5 都不适合直接调用带密钥的第三方 API:前端代码无法真正保密,两端还各受域名白名单与同源策略限制。更稳妥的做法是在自己的服务端加一层代理,集中处理鉴权、超时、错误码归一化和日志脱敏,前端只调用同源接口。文中给出了代理层需要先定的边界、一个未执行的最小示例,以及上线前的核实清单。
结论先说:在小程序和 H5 里,第三方 API 的调用点应该放在自己的服务端,前端只调用自己的同源接口。这样一次解决三件事——密钥不出服务端、浏览器同源策略不再挡路、第三方错误码只在一个地方归一化。需要说明的是,本文没有 API.PLUS 的接口文档正文,因此不涉及任何具体端点、参数与价格;这些信息待核实后再补。
前端直连会碰到什么#
密钥藏不住
前端代码最终运行在用户设备上,压缩混淆不等于加密。只要密钥出现在小程序包或网页资源里,就要按“已经泄露”来设计。判断很直接:凡是需要在请求头里携带服务端密钥的第三方接口,都不适合前端直连。
小程序受域名限制
按微信小程序当前的网络请求规则,可请求的域名需要事先在后台配置,通常要求 HTTPS,具体条款、端口限制和审核要求以微信官方文档为准(本次未核验最新版本,属待核实项)。这条规则带来的实际成本是:换域名、按用户切换上游、灰度新接口,都要走一次配置与发布流程。
H5 受同源策略限制
浏览器会按同源策略检查跨域请求:上游如果没有返回允许跨域的响应头,浏览器会在页面读到响应之前就拦截,具体是否触发预检取决于请求方法和请求头。小程序不走浏览器的同源策略,但受上面的域名限制——两端的约束并不相同,所以不存在一套通用的“直连”写法。
代理层先定这几件事#
- 密钥位置:只放在服务端环境变量或密钥管理服务里,不进代码仓库、不进前端包。
- 入参裁剪:代理不是透传。对允许的字段做白名单,限制长度与类型,避免前端把任意参数直接打到上游。
- 超时与取消:给上游设明确超时,超时就断开,不要让请求无限挂起占用连接。
- 重试边界:只对幂等请求重试,重试次数与退避策略固定在服务端;不要在前端叠加自动重试,否则会放大上游压力。
- 错误归一:把上游的错误码映射成自己的少量状态,前端只认这一套。
- 响应裁剪与日志脱敏:只回传前端需要的字段;日志里不记录完整密钥、用户标识等敏感内容。
一个最小代理示例#
下面是一段 Node.js(Express + 内置 fetch,Node 18 及以上)的演示代码,未实际执行,域名、路径、字段和状态码均为占位,仅用于说明结构;上游密钥通过环境变量注入,示例中写作 YOUR_KEY 的位置不可替换为真实值。
// 演示代码,未执行;api.example.com 与字段均为占位
import express from "express";
const app = express();
app.post("/api/proxy/echo", express.json(), async (req, res) => {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 5000);
try {
const upstream = await fetch("https://api.example.com/v1/echo", {
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${process.env.UPSTREAM_KEY}` // 对应 YOUR_KEY
},
body: JSON.stringify({ text: String(req.body.text ?? "").slice(0, 200) }),
signal: controller.signal
});
const data = await upstream.json();
// 只回传前端需要的字段,不透传整个上游响应
res.status(upstream.ok ? 200 : 502).json({ ok: upstream.ok, data: upstream.ok ? data : null });
} catch (e) {
res.status(504).json({ ok: false, code: "UPSTREAM_TIMEOUT" });
} finally {
clearTimeout(timer);
}
});
app.listen(3000);
上线前值得核对的几项#
- 小程序后台配置的请求域名是否覆盖当前代理地址,是否已随发布计划更新。
- H5 与小程序是否调用同一套代理接口;若分两套,错误码是否一致。
- 代理是否把上游的鉴权失败与业务失败区分开,前端能否据此决定“提示用户”还是“重试”。
- 日志中是否能定位一次失败请求,同时不包含密钥与完整用户数据。
- 上游限流口径与自己的配额是否对得上(对象、时间窗、单位),不确定就按待核实处理,不要凭感觉写结论。
至于具体调用 API.PLUS 时该用哪种鉴权方式、错误体长什么样、是否有并发或频率上限,本文没有可依据的正文材料,不做陈述。这些应在拿到官方文档后逐项核实,再决定代理层的字段映射。