You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套Promise调用Ball Don't Lie API遇301错误及CORS问题

NBA球员查询功能CORS问题解决指南

问题核心

你遇到的301+CORS错误,根源有两个:

  1. 第二个请求的URL用了不带www的balldontlie.io,服务器会301重定向到带www的版本,浏览器跨域请求时会检查重定向后的响应头,触发CORS报错
  2. 前端直接调用第三方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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 04:35:47