Next.js调用Spotify授权API遭遇CORS跨域拦截问题求助
问题描述
在Next.js应用中调用Express搭建的Spotify授权接口时触发CORS错误:
从源'http://localhost:3000'访问'https://accounts.spotify.com/authorize?xxx'(重定向自'http://localhost:8888/api/login')被CORS策略阻止:请求的资源上不存在'Access-Control-Allow-Origin'头。若无需透明响应,可将请求模式设为'no-cors'以禁用CORS。
直接在浏览器地址栏访问Express接口正常,部署到Vercel也无问题,但Next.js中用fetch调用就报错。
问题根源
CORS策略仅限制跨域AJAX/fetch请求,而浏览器直接导航(地址栏输入、页面跳转)不受此限制。你之前用fetch调用/api/login,fetch会自动跟随重定向到Spotify的授权页面,但Spotify的授权页面没有配置允许localhost:3000的CORS头,因此触发错误。
解决方案
1. 前端替换调用方式:用页面跳转代替fetch
不要在Next.js中用fetch请求/api/login,而是直接通过页面跳转触发授权流程:
// Next.js组件中的登录触发函数 const handleSpotifyLogin = () => { window.location.href = 'http://localhost:8888/api/login'; };
2. 调整Express回调路由:处理授权后重定向回Next.js
在/api/callback路由中,获取到Spotify的access_token并拿到当前播放歌曲后,不要直接返回JSON,而是重定向回Next.js页面,同时将歌曲数据存在服务端session(需安装express-session中间件):
const callback = require('express').Router(); const querystring = require('querystring'); const request = require('request'); const session = require('express-session'); require('dotenv').config(); // 配置session(需提前在Express主文件中注册session中间件) callback.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: true, cookie: { secure: process.env.NODE_ENV === 'production' } })); callback.get('/', (req, res) => { // ... 原有获取access_token的逻辑 ... const options = { url: 'https://api.spotify.com/v1/me/player/currently-playing', headers: { 'Authorization': 'Bearer ' + access_token }, json: true }; request.get(options, function(error, response, body) { // 将歌曲数据存入session req.session.currentSong = body?.item ? { name: body.item.name, artist: body.item.artists[0].name, album: body.item.album.name, image: body.item.album.images[0].url } : { name: '', artist: '', album: '', image: '' }; // 重定向回Next.js的目标页面 res.redirect('http://localhost:3000/now-playing'); }); });
3. 新增Express接口:供Next.js获取当前歌曲数据
创建/api/get-current-song接口,从session中读取歌曲数据并返回给Next.js:
const currentSongRouter = require('express').Router(); const cors = require('cors'); currentSongRouter.use(cors({ origin: 'http://localhost:3000', credentials: true // 允许携带cookie(session依赖cookie) })); currentSongRouter.get('/', (req, res) => { res.json(req.session.currentSong || { name: '', artist: '', album: '', image: '' }); }); module.exports = currentSongRouter;
4. Next.js中获取歌曲数据
在Next.js的/pages/now-playing.js页面中,调用新增的接口获取数据:
import { useEffect, useState } from 'react'; export default function NowPlaying() { const [songData, setSongData] = useState({}); useEffect(() => { const fetchCurrentSong = async () => { try { const res = await fetch('http://localhost:8888/api/get-current-song', { credentials: 'include' // 携带session cookie }); const data = await res.json(); setSongData(data); } catch (error) { console.error('获取当前歌曲失败:', error.message); } }; fetchCurrentSong(); }, []); return ( <div> <h2>当前播放</h2> {songData.image && <img src={songData.image} alt={songData.name} width={200} />} <p>歌曲:{songData.name}</p> <p>歌手:{songData.artist}</p> <p>专辑:{songData.album}</p> </div> ); }
额外优化建议
- 生产环境中,将
origin配置为你的Next.js域名,不要用* - 使用
express-session时,生产环境需配置安全的cookie(secure: true)和合适的存储(如Redis),避免内存泄漏 - 可以将Spotify的access_token存在session中,后续调用Spotify API时直接从session读取,无需重复授权
内容的提问来源于stack exchange,提问作者Jose Izarra

