求助:Spotify API POST请求失败及401未授权问题排查
解决Spotify PKCE认证400及获取Top艺术家401错误
核心问题分析
- POST请求400错误:大概率是
code_challenge生成不符合Spotify的PKCE规范,或者重定向URI与后台配置不一致。 - GET请求401错误:
redirectToken是异步函数,直接将其返回值作为token会导致Authorization头无效(实际是Bearer [object Promise]),且未等待token获取完成就发起请求。
第一步:修复PKCE流程(解决400错误)
首先确保generateRandomString和generateCodeChallenge函数完全符合Spotify要求:
code_verifier必须是43-128位的ASCII字符,仅包含a-z, A-Z, 0-9, -, ., _, ~code_challenge是code_verifier的SHA-256哈希值,再经过base64url编码(去掉末尾的=,替换+为-,/为_)
添加正确的辅助函数:
function generateRandomString(length) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'; let result = ''; const array = new Uint32Array(length); window.crypto.getRandomValues(array); for (let i = 0; i < length; i++) { result += chars[array[i] % chars.length]; } return result; } async function generateCodeChallenge(codeVerifier) { const encoder = new TextEncoder(); const data = encoder.encode(codeVerifier); const digest = await window.crypto.subtle.digest('SHA-256', data); // 转换为base64url格式 return btoa(String.fromCharCode(...new Uint8Array(digest))) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); }
同时确认:
- Spotify开发者后台的重定向URI与代码中的
redirectUri完全一致(包括http://127.0.0.1:5500/index.html的协议、端口、路径) - 每次认证流程中,
code只能使用一次,避免重复提交
第二步:修复异步调用问题(解决401错误)
redirectToken是异步函数,需要返回Promise以便后续等待token获取完成;getTopArtists需要在拿到有效token后再发起请求。
修正后的redirectToken函数:
function redirectToken() { return new Promise((resolve, reject) => { const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); if (!code) { reject(new Error('未获取到授权code')); return; } const codeVerifier = localStorage.getItem('code-verifier'); const redirectUri = 'http://127.0.0.1:5500/index.html'; const clientId = "f101df3a42484b9e9aa1d1aa34fa5770"; const body = new URLSearchParams({ grant_type: 'authorization_code', code: code, redirect_uri: redirectUri, client_id: clientId, code_verifier: codeVerifier }); fetch('https://accounts.spotify.com/api/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body }) .then(response => { if (!response.ok) { throw new Error('HTTP状态码: ' + response.status); } return response.json(); }) .then(data => { localStorage.setItem('access-token', data.access_token); resolve(data.access_token); // 返回token供后续使用 }) .catch(error => { console.error('获取token失败:', error); reject(error); }); }); }
修正后的getTopArtists函数:
async function getTopArtists() { // 优先从localStorage取token,没有则调用redirectToken获取 let accessToken = localStorage.getItem('access-token'); if (!accessToken) { try { accessToken = await redirectToken(); } catch (err) { console.error('无法获取有效token:', err); return; } } const url = "https://api.spotify.com/v1/me/top/artists?limit=3&offset=0&time_range=short_term"; // 注意:offset=3会跳过前3个,要获取TOP3应该设为offset=0 const xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.setRequestHeader("Authorization", "Bearer " + accessToken); xhr.onload = function() { if (xhr.status === 200) { const data = JSON.parse(xhr.responseText); console.log('TOP3艺术家:', data.items); } else { console.error('请求失败,状态码:', xhr.status); // 如果token过期,可触发重新认证 if (xhr.status === 401) { localStorage.removeItem('access-token'); authenticate(); } } }; xhr.onerror = function() { console.error('网络请求错误'); }; xhr.send(); }
调用流程说明
- 用户点击认证按钮触发
authenticate(),跳转至Spotify授权页 - 授权完成后重定向回页面,调用
redirectToken()获取并存储token - 调用
getTopArtists()时,会自动获取token并发起请求
额外提示:你的getTopArtists中offset=3会跳过前3个艺术家,若要获取真正的TOP3,需改为offset=0。
内容的提问来源于stack exchange,提问作者PaperBro64
相关产品推荐
相关产品推荐

