调用Deezer API遇CORS错误:单请求正常双请求失败如何解决?
在React项目中自定义useFetch Hook,使用axios的Promise.all同时发起两个Deezer API请求时触发CORS错误,但仅发起单个请求时可正常获取数据,Postman测试接口能成功得到响应。
错误提示:
Access to XMLHttpRequest at 'https://api.deezer.com/artist/145/top?limit=10' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
自定义useFetch Hook代码:
export const useFetch = (url, url2) => { const [isLoading, setIsloading] = useState(false); const [apiData, setApiData] = useState(null); const [isError, setIsError] = useState(null); useEffect(() => { setIsloading(true); const allPromise = Promise.all([ axios.get(url, { headers: { "X-RapidAPI-Key": process.env.REACT_APP_API_KEY, "X-RapidAPI-Host": process.env.REACT_API_HOST, "Access-Control-Allow-Origin": "https://localhost:3000/", }, mode: "cors", }), axios.get(url2, { headers: { "X-RapidAPI-Key": process.env.REACT_APP_API_KEY, "X-RapidAPI-Host": process.env.REACT_API_HOST, "Access-Control-Allow-Origin": "https://localhost:3000/", }, mode: "cors", }), ]); try { const values = allPromise; console.log(values); // [resolvedValue1, resolvedValue2] setApiData(values); } catch (error) { console.log(error); // rejectReason of any first rejected promise } }, [url]); return { isLoading, apiData, isError }; };
组件调用代码:
const SongList = () => { const { isLoading, apiData, isError } = useFetch( process.env.REACT_APP_API_URL_PLAYLIST, process.env.REACT_APP_API_URL ); console.log(apiData); }
- 请求头配置错误:你在请求中添加了
Access-Control-Allow-Origin头,这个头是服务器返回给浏览器的响应头,不是前端请求时需要发送的。前端发送这个多余的头会触发浏览器的预检OPTIONS请求,而Deezer API服务器未处理该预检请求,直接导致CORS报错。 - Promise异步处理错误:
Promise.all返回的是Promise对象,你直接将其赋值给values,没有等待异步请求完成,不仅无法拿到实际响应数据,try/catch也无法正确捕获请求错误。 - useEffect依赖缺失:
useEffect的依赖数组仅包含url,但Hook逻辑中用到了url2,当url2变化时不会触发重新请求,还可能导致请求逻辑异常。 - 并发预检请求限制:部分API对并发的预检请求有频率限制,同时发起两个请求会触发两次预检,可能被服务器判定为异常请求而拒绝。
1. 修正请求头配置
移除请求中的Access-Control-Allow-Origin头和多余的mode: "cors"(axios默认采用cors模式),正确的请求配置如下:
axios.get(url, { headers: { "X-RapidAPI-Key": process.env.REACT_APP_API_KEY, "X-RapidAPI-Host": process.env.REACT_APP_API_HOST, }, })
2. 正确处理Promise异步逻辑
使用async/await配合try/catch/finally处理异步请求,确保能正确获取数据、捕获错误,并在请求结束后更新加载状态:
useEffect(() => { const fetchData = async () => { setIsloading(true); try { const [res1, res2] = await Promise.all([ axios.get(url, { headers: { "X-RapidAPI-Key": process.env.REACT_APP_API_KEY, "X-RapidAPI-Host": process.env.REACT_APP_API_HOST, }, }), axios.get(url2, { headers: { "X-RapidAPI-Key": process.env.REACT_APP_API_KEY, "X-RapidAPI-Host": process.env.REACT_APP_API_HOST, }, }), ]); setApiData([res1.data, res2.data]); // 存储实际响应数据 setIsError(null); } catch (error) { setIsError(error); console.error("请求失败:", error); } finally { setIsloading(false); // 请求结束后无论成败都关闭加载状态 } }; fetchData(); }, [url, url2]); // 添加url2到依赖数组,确保参数变化时重新请求
3. 代理解决CORS问题(终极方案)
如果修正请求后仍出现CORS错误,说明Deezer API本身不允许前端直接跨域请求,此时需要使用代理:
- React内置代理:在
package.json中添加"proxy": "https://api.deezer.com",前端请求时使用相对路径,比如axios.get("/artist/145/top?limit=10") - 自定义后端代理:用Node.js/Express搭建简单代理服务器,前端请求自己的后端,后端再转发请求到Deezer API,彻底避开浏览器的跨域限制。
4. 降级为串行请求
如果并发请求仍触发服务器限制,可以改成串行请求(虽速度稍慢,但能避免并发预检问题):
const fetchData = async () => { setIsloading(true); try { const res1 = await axios.get(url, { /* 配置 */ }); const res2 = await axios.get(url2, { /* 配置 */ }); setApiData([res1.data, res2.data]); } catch (error) { setIsError(error); } finally { setIsloading(false); } };
内容的提问来源于stack exchange,提问作者Xena

