Next.js/React调用Pinterest API遇CORS拦截,已加白名单仍无效
问题详情
你在Next.js/React应用中调用Pinterest API获取授权Token时遇到CORS错误,相关代码如下:
const redirectUrl = 'http://localhost:3000/pinterest'; const clientId = '1234'; const clientSecret = 'XXXXXXXXXXX'; let url = 'https://api.pinterest.com/v5/oauth/token'; let body = { 'code': code, 'grant_type': 'authorization_code', 'redirect_uri': redirectUrl }; let accessTokenRequestBody = Object.keys(body) .map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(body[k])}`) .join('&'); const clientIdAndSecretBase64 = Buffer.from(`${clientId}:${clientSecret}`).toString('base64'); try { let response = await axios.post(url, accessTokenRequestBody, { "headers": { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': `Basic ${clientIdAndSecretBase64}` } }) console.log(response) } catch (e) { console.log("error") console.log(e?.reponse?.data) }
错误信息:
从源'http://localhost:3000'访问'https://api.pinterest.com/v5/oauth/token'的XMLHttpRequest被CORS策略阻止:预检请求的响应未通过访问控制检查:请求的资源上不存在'Access-Control-Allow-Origin'标头。
你已将localhost:3000和localhost:3000/pinterest添加到Pinterest应用白名单,但问题依旧,以下是可能的原因及解决办法:
可能的原因及解决办法
1. Token接口本身禁止前端直接调用
Pinterest的/v5/oauth/token接口是专门给后端服务设计的,不允许前端浏览器直接请求。因为这个接口需要用到client_secret,如果在前端暴露这个密钥,会导致你的应用权限被恶意滥用,所以Pinterest不会给这个接口配置CORS允许头,无论你加多少白名单都没用。
2. 白名单配置的是授权回调地址,不是API请求源
你在Pinterest后台添加的域名是OAuth授权流程的回调地址——也就是用户授权后Pinterest跳转回你应用的地址,和API请求的CORS白名单完全是两回事。Pinterest的API接口(包括Token接口)并不支持配置前端请求源的CORS白名单。
3. 正确解决方式:用Next.js API路由做代理
把请求逻辑移到Next.js的后端API路由里,前端调用自己的后端接口,再由后端转发请求到Pinterest,这样就绕开了浏览器的CORS限制:
示例代码(Pages Router)
创建pages/api/pinterest-token.js:
import axios from 'axios'; export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ message: '仅支持POST请求' }); } const { code } = req.body; const redirectUrl = 'http://localhost:3000/pinterest'; const clientId = '1234'; const clientSecret = 'XXXXXXXXXXX'; // 这里的密钥只会在后端暴露,安全 const url = 'https://api.pinterest.com/v5/oauth/token'; const body = { code, grant_type: 'authorization_code', redirect_uri: redirectUrl }; const accessTokenRequestBody = Object.keys(body) .map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(body[k])}`) .join('&'); const clientIdAndSecretBase64 = Buffer.from(`${clientId}:${clientSecret}`).toString('base64'); try { const response = await axios.post(url, accessTokenRequestBody, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': `Basic ${clientIdAndSecretBase64}` } }); res.status(200).json(response.data); } catch (e) { res.status(e.response?.status || 500).json(e.response?.data || { message: '请求失败' }); } }
前端调用修改
try { const response = await axios.post('/api/pinterest-token', { code }); console.log('获取到的Token:', response.data); } catch (e) { console.error('获取Token失败:', e); }
4. 额外注意事项
- 绝对不要在前端代码里写
clientSecret,避免密钥泄露。 - 生产环境部署时,记得把
redirectUrl改成你的生产域名,同时在Pinterest后台更新回调地址配置。
内容的提问来源于stack exchange,提问作者antonwilhelm

