微信小程序与 H5 调用第三方 API:先定密钥归属,再选直连或代理
小程序不走浏览器同源策略,卡点在后台配置的 request 合法域名与 HTTPS;H5 受浏览器 CORS 约束。两者共同的决定因素是密钥放在哪一侧:凡涉及 AppSecret 或长期 API Key,就应走自有服务端代理,而不是在客户端直连。
结论先给:微信小程序和 H5 调用第三方 API,失败原因通常不在写法,而在两件与代码无关的事——请求从哪个环境发出,以及密钥放在哪一侧。小程序没有浏览器的同源策略,真正的门槛是小程序后台配置的服务器域名白名单;H5 运行在浏览器里,真正门槛是第三方是否返回允许跨域的响应头。两者都涉及长期密钥时,正确姿势都是加一层自有服务端代理。
本文不针对某个具体接口的可用性下判断。API.PLUS 各接口是否支持浏览器直连、是否需要额外签名,需以其接口文档为准,本次未联网核验,相关细节待核实。
小程序:卡在白名单,不是 CORS#
小程序的网络请求由客户端发起,不受浏览器同源策略约束,因此不存在 CORS 预检。取而代之的是域名白名单机制:需要在微信公众平台后台为相应类目(如 request、uploadFile、downloadFile、socket)配置合法域名,且域名一般要求为 HTTPS、不能使用 IP 地址,备案等要求以微信官方文档为准。未配置的域名在真机上会直接失败,而开发者工具里勾选「不校验合法域名」后能跑通——这个开关只用于本地调试,不能作为上线依据。
需要强调的是,白名单只解决「能不能发出去」,不解决「该不该从这里发」。小程序代码包可以被解出,把 AppSecret 或长期有效的 API Key 写进前端,等同于公开。涉及签名的调用应放在服务端完成,客户端只拿短期凭证。
H5:CORS 是浏览器在执行#
CORS(Cross-Origin Resource Sharing,跨源资源共享)的检查由浏览器完成,服务端只是提供响应头。常见误区有三个:
- 简单请求与预检请求不同。带自定义头(如
Authorization)或非简单 Content-Type 时,浏览器会先发OPTIONS预检,服务端必须正确响应,否则真实请求根本不会发出。 Access-Control-Allow-Origin取*时不能同时携带凭证(cookies);需要凭证就要回显具体来源并配Allow-Credentials。- 用 curl 或 Postman 调通,不能证明浏览器能调通——这些工具不执行 CORS 检查。
两种环境,同一套判断顺序#
| 判断项 | 微信小程序 | H5 |
|---|---|---|
| 拦截面 | 后台域名白名单、HTTPS | 浏览器同源策略与预检 |
| 密钥能否放前端 | 不能 | 不能 |
| 推荐的稳定形态 | 客户端 → 自有服务端 → 第三方 | 同左,或第三方明确支持且仅用公开只读凭证 |
| 调试期常用手段 | 关闭域名校验(仅本地) | 关闭浏览器安全策略(仅本地) |
表格最后一行值得单独说:两种「关闭校验」的做法都只应存在于开发机,不能进构建产物,也不能作为「线上也行」的证据。
代理链路的顺序#
如果决定走自有服务端,顺序建议是:客户端携带短期身份凭证 → 服务端校验身份与配额 → 服务端拼接第三方密钥并签名 → 请求第三方 → 服务端归一化响应后再返回客户端。这样客户端永远看不到长期密钥,CORS 与域名白名单问题也一并消解,因为客户端只和自己的域名通信。
需要一并定下的是失败路径:超时设多久、是否重试、重试是否会重复计费。第三方接口是否幂等通常未知,未确认前不宜自动重试写操作。
未执行的示例骨架
以下两段均为未执行的示例,仅示意结构,端点与字段均为占位,预期行为未经验证。
// 小程序端:只请求自己的域名,密钥不出现在这里
wx.request({
url: 'https://api.example.com/proxy/query', // 占位
method: 'POST',
header: { 'content-type': 'application/json' },
data: { q: 'demo', token: 'YOUR_SESSION_TOKEN' },
success: (res) => { /* 按自有服务端的统一契约解析 */ },
fail: (err) => { /* 网络失败与业务失败分开处理 */ }
});
// 服务端转发:密钥来自环境变量,不下发到客户端
app.post('/proxy/query', async (req, res) => {
// 1) 校验 req.body.token 2) 配额与限流 3) 拼接第三方密钥
const upstream = await fetch('https://third-party.example.com/v1/query', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.UPSTREAM_KEY}` },
body: JSON.stringify({ q: req.body.q })
});
res.status(upstream.status).json(await upstream.json());
});
边界条件#
如果第三方 API 只提供浏览器端公开只读凭证、且明确声明支持跨域,H5 直连可以作为简化方案,但仍要接受它的限制:凭证公开、无法做服务端限流、第三方策略变更会直接影响前端。反过来,如果第三方完全不支持 CORS,那么无论 H5 代码怎么写都调不通,代理是唯一可行路径,这一点不需要反复尝试来确认。
最后,小程序域名白名单的具体规则、H5 各浏览器的预检缓存时长,均以微信官方文档与浏览器厂商文档为准,本文不给出具体数值。