嵌套Promise调用Ball Don't Lie API遇301错误及CORS问题
NBA球员查询功能CORS问题解决指南
问题核心
你遇到的301+CORS错误,根源有两个:
- 第二个请求的URL用了不带
www的balldontlie.io,服务器会301重定向到带www的版本,浏览器跨域请求时会检查重定向后的响应头,触发CORS报错 - 前端直接调用第三方API时,自身Express服务的CORS配置只对自有接口生效,无法解决第三方API的跨域限制
修复步骤
方案1:快速修复URL(临时解决)
把第二个fetch的URL改成和第一个一致的带www地址,避免301重定向:
document.querySelector('button').addEventListener('click', apiRequest) function apiRequest() { const player = document.querySelector('input').value; const season = document.querySelector('input[type="number"]').value; let id; fetch(`https://www.balldontlie.io/api/v1/players/?search=${player}`) .then(res => res.json()) .then(data => { // 先判断是否找到球员,避免空指针报错 if (!data.data || data.data.length === 0) { throw new Error('未找到该球员'); } const playerData = data.data[0]; id = playerData.id; document.querySelector('h2').innerText = `${playerData.first_name} ${playerData.last_name}`; document.querySelector('.team').innerText = playerData.team.full_name; document.querySelector('.position').innerText = playerData.position; return id; // 把id传递到下一个then }) .then(playerId => { // 修复URL,添加www前缀 return fetch(`https://www.balldontlie.io/api/v1/stats?season[]=${season}&player_ids[]=${playerId}`) .then(res => res.json()) }) .then(statsData => { console.log(statsData); // 在这里处理统计数据并更新页面 }) .catch(err => { console.error('请求出错:', err); }) }
方案2:用Express做代理(推荐,彻底解决跨域)
前端不再直接调用第三方API,而是请求自身Express服务器,由服务器转发请求到Ball Don't Lie API,规避浏览器同源策略限制:
1. 安装依赖
npm install node-fetch
2. 修改server.js
const express = require('express'); const fetch = require('node-fetch'); const app = express(); const PORT = 8000; const cors = require('cors'); app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static("public")); // 代理球员查询请求 app.get('/api/players', async (req, res) => { try { const search = req.query.search; const response = await fetch(`https://www.balldontlie.io/api/v1/players/?search=${search}`); const data = await response.json(); res.json(data); } catch (err) { res.status(500).json({ error: err.message }); } }); // 代理球员统计数据请求 app.get('/api/stats', async (req, res) => { try { const season = req.query.season; const playerId = req.query.player_id; const response = await fetch(`https://www.balldontlie.io/api/v1/stats?season[]=${season}&player_ids[]=${playerId}`); const data = await response.json(); res.json(data); } catch (err) { res.status(500).json({ error: err.message }); } }); app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); }) app.listen(PORT, () => { console.log(`服务器运行在端口 ${PORT}`); })
3. 修改前端代码
document.querySelector('button').addEventListener('click', apiRequest) function apiRequest() { const player = document.querySelector('input').value; const season = document.querySelector('input[type="number"]').value; // 请求自身服务器的代理接口 fetch(`/api/players?search=${player}`) .then(res => res.json()) .then(data => { if (!data.data || data.data.length === 0) { throw new Error('未找到该球员'); } const playerData = data.data[0]; document.querySelector('h2').innerText = `${playerData.first_name} ${playerData.last_name}`; document.querySelector('.team').innerText = playerData.team.full_name; document.querySelector('.position').innerText = playerData.position; return playerData.id; }) .then(playerId => { return fetch(`/api/stats?season=${season}&player_id=${playerId}`) .then(res => res.json()) }) .then(statsData => { console.log(statsData); // 处理统计数据并展示到页面 }) .catch(err => { console.error('请求出错:', err); }) }
额外注意事项
- 始终检查API返回的数据结构,避免
data.data[0]这类写法导致的空指针错误 - 变量使用
const/let声明,避免全局变量污染 - Ball Don't Lie API有请求频率限制,注意不要超过限制
内容的提问来源于stack exchange,提问作者Nichelle
相关产品推荐
相关产品推荐

