微信小程序与 H5 调用第三方 API:把密钥留在服务端,再处理跨域与域名
小程序与 H5 直连第三方 API 会同时遇到密钥暴露、跨域或域名白名单限制以及无法统一限流的问题。更稳妥的做法是让客户端只调用自己的后端,由后端持密钥、做超时与错误映射,再按平台规则处理小程序合法域名与 H5 的 CORS。具体平台规则与第三方接口细节需以其官方文档为准,本文未做核验。
- 客户端不持有第三方长期密钥,密钥只放在服务端环境变量里。
- 小程序受合法域名等平台配置约束,H5 受浏览器同源策略与 CORS 约束,两者都不该用客户端直连来绕过。
- 后端中转要显式设置超时、把上游错误映射成自己的错误码,并给出降级路径。
简短回答:不要把第三方 API 的调用写在微信小程序或 H5 的前端代码里。让客户端只请求你自己的后端,由后端持有密钥、发起上游请求、统一超时与错误处理,再决定小程序侧的合法域名配置和 H5 侧的跨域策略。本文只讨论这一层的工程判断;微信平台的具体规则、第三方服务的鉴权方式与配额,均需以其官方文档为准,本次未核验。
直连的三个问题#
密钥暴露。小程序包可被解包,H5 的请求在浏览器网络面板里一览无余。任何写进前端的 AppKey、Token 都应视为已公开。
运行环境限制。H5 受同源策略约束:浏览器是否允许页面直接读取第三方响应,取决于对方是否返回合适的 CORS 响应头,以及预检请求(OPTIONS)是否被正确处理。这不是前端代码能单方面决定的事。
失去控制点。直连意味着限流、缓存、审计、故障降级都散落在客户端,出问题时既无法统一关闭,也无法统计。
小程序侧:先看平台规则,再谈代码#
小程序的网络请求走宿主提供的 API,而不是浏览器 fetch,因此不适用 CORS,但通常受平台侧的网络请求域名配置约束,且一般要求 HTTPS。具体是哪些域名需要提前配置、是否支持通配、开发与体验版是否放宽,属于平台规则,请以微信官方文档为准;本文不给出未经核验的结论。
可以确定的工程结论是:既然域名需要提前登记,就更应把登记对象设为你自己的后端域名,而不是随第三方变更而变动的外部域名。也不要试图用内嵌网页等方式绕过请求约束——那只是把同一个问题挪到另一个运行环境里。
H5 侧:跨域是对方的策略,不是你的配置#
如果第三方明确为浏览器场景提供 CORS 支持,直连在技术上可行;但此时密钥依然暴露,通常仍建议中转。若对方未开放跨域,前端无论如何配置都拿不到响应体,只能看到被浏览器拦截的失败。判断依据是对方文档与响应头,而不是本地试出来的结果。
推荐结构:客户端 → 你的后端 → 第三方#
- 密钥放在服务端环境变量,不进仓库、不进构建产物。
- 后端为每个上游接口设置显式超时,并使用可取消的请求。
- 把上游状态码与业务错误码翻译成你自己稳定的错误码,客户端只认这一套。
- 在网关上做限流与缓存,客户端不承担这部分逻辑。
- 为客户端定义降级行为:返回缓存、占位内容,或明确的“稍后重试”。
// 未执行示例:Node.js + Express 结构示意,未实际运行
app.get('/api/quote', async (req, res) => {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 5000);
try {
const r = await fetch('https://api.example.com/v1/quote', {
headers: { Authorization: 'Bearer ' + process.env.UPSTREAM_KEY },
signal: controller.signal
});
if (!r.ok) return res.status(502).json({ code: 'UPSTREAM_ERROR' });
res.json(await r.json());
} catch (e) {
res.status(504).json({ code: 'UPSTREAM_TIMEOUT' });
} finally {
clearTimeout(timer);
}
});
// 未执行示例:微信小程序端调用自己的后端
wx.request({
url: 'https://your-domain.example.com/api/quote',
method: 'GET',
timeout: 6000,
success(res) { /* 只依赖自己的返回结构与错误码 */ },
fail() { /* 走降级:缓存内容或占位提示 */ }
});
示例中的 example.com、your-domain.example.com 与 UPSTREAM_KEY 均为占位,需替换为已核实的真实地址与密钥管理方式。两段代码均未执行,不构成实测结论。
上线前的核对项#
| 核对项 | 要确认什么 |
|---|---|
| 密钥位置 | 前端产物、日志、错误上报中均不出现密钥 |
| 平台配置 | 小程序侧域名是否已按平台要求登记,以官方文档为准 |
| 跨域 | 若前端需直连,对方是否明确支持 CORS;否则不列入方案 |
| 超时与重试 | 超时值、是否幂等、重试次数上限是否有明确约定 |
| 错误语义 | 客户端是否能区分“暂时失败”和“请求本身有错” |
若你接入的是 API.PLUS 这类第三方接口服务,鉴权方式、请求配额、返回结构与是否允许浏览器直连,都要以其文档为准;本文材料未包含这些细节,均待核实。可以先行确定的是架构边界:密钥留在服务端,客户端只面对你定义的接口契约,平台规则与上游变更就不会直接传导到用户界面。