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

React调用Spotify API获取播放列表时遭遇403 Forbidden错误的排查求助

React调用Spotify API获取播放列表时遭遇403 Forbidden错误的排查求助

我在使用React开发时,尝试通过Spotify API获取用户的播放列表ID,但遇到了403 Forbidden错误,具体错误信息如下:

  • Failed to load resource: the server responded with a status of 403 ()
  • Error fetching (the Spotify playlist id): 403

我已经排查了以下几点,确认功能正常:

  • 访问令牌(access token)可以正常使用
  • 刷新令牌(refresh token)可以正常使用
  • 存储在localStorage中的access token和refresh token均有效

我的Token组件代码:

async function refreshAccessToken(refreshToken) {
  try {
    const response = await fetch("https://accounts.spotify.com/api/token", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        Authorization: `Basic ${btoa("(client id):(client secret)")}`,
      },
      body: new URLSearchParams({
        grant_type: "refresh_token",
        refresh_token: refreshToken,
      }),
    });

    const data = await response.json();
    console.log("Spotify API Response:", data);

    const accessToken = data.access_token;

    const playlistResponse = await fetch("https://api.spotify.com/v1/me/playlists", {
        method: "GET",
        headers: {
          Authorization: `Bearer ${accessToken}`, 
        },
      });
          const playlists = await playlistResponse.json();
        console.log(playlists)
    if (data.access_token) {
      localStorage.setItem("spotifyAccessToken", data.access_token);
      return data.access_token;
    }
  } catch (error) {
    console.error("Error refreshing token:", error);
  }

}


function Token() { 
  const [accessToken, setAccessToken] = 
useState(localStorage.getItem("spotifyAccessToken") || "");
  const [refreshToken, setRefreshToken] = 
useState(localStorage.getItem("spotifyRefreshToken") || "");

  useEffect(() => {
    console.log("Updated access token:", accessToken);
  }, [accessToken]);

  useEffect(() => {
    const storedAccessToken = localStorage.getItem("spotifyAccessToken");
    const storedRefreshToken = localStorage.getItem("spotifyRefreshToken");
    console.log("Stored Access Token:", localStorage.getItem("spotifyAccessToken"));
    console.log("Stored Refresh Token:", localStorage.getItem("spotifyRefreshToken"));

    if (!storedAccessToken || !storedRefreshToken) {
       const token = "(my access token)";
       const refresh = "(my refresh token)"; 

      localStorage.setItem("spotifyAccessToken", token);
      localStorage.setItem("spotifyRefreshToken", refresh);
      setAccessToken(token);
      setRefreshToken(refresh);
    }

    if (storedRefreshToken) {
      const fetchNewToken = async () => {
        const newToken = await refreshAccessToken(storedRefreshToken);
        if (newToken) {
          setAccessToken(newToken);
          localStorage.setItem("spotifyAccessToken", newToken);
        }
      };
      fetchNewToken();
    }
  }, []);


  useEffect(() => {
    const interval = setInterval(async () => {
      const newToken = await refreshAccessToken(refreshToken);
      if (newToken) setAccessToken(newToken);
    }, 1000 * 60 * 50); 

    return () => clearInterval(interval);
  }, [refreshToken]);

  return null; 
}

export default Token;

我的Home组件代码(用于获取播放列表数据):

const [playlists, setPlaylists] = useState([]);
  const [accessToken, setAccessToken] = 
useState(localStorage.getItem("spotifyAccessToken") || "");
  useEffect(() => {
    const handleStorageChange = () => {
      const newToken = localStorage.getItem("spotifyAccessToken");
      if (newToken) setAccessToken(newToken);
    };

    window.addEventListener("storage", handleStorageChange);
    return () => window.removeEventListener("storage", handleStorageChange);
  }, []);
  console.log("Access Token:", accessToken);


   const playlistIds = useMemo(() => [
   "2TA2qT9p2e91lGZEozyGlI",
   "4Ap2xIU37l1NAShncyota",
   "0Y44bc2Fd2IfbhqSPnNlTC",
   "6jA8JKtuPrsNCTbnvgYkBt",
   "4GX8ccKz938Q2BtF8NMdX2",
   "37i9dQZF1DZ06evO2yXXGB"
  ], []);


  useEffect(() => {
    if (!accessToken) {
      console.warn("No access token available. Cannot fetch playlists.");
      return;
    }

    const fetchPlaylists = async () => {
      try {
        const responses = await Promise.all(
          playlistIds.map(async (id) => {
            const response = await fetch(`https://api.spotify.com/v1/playlists/${id}`, 
{
              headers: { Authorization: `Bearer ${accessToken}` },
            });

            if (!response.ok) {
              console.warn(`Error fetching ${id}: ${response.status} 
${response.statusText}`);
              if (response.status === 403) return { error: "Forbidden: Check your Spotify scopes." };
              if (response.status === 401) return { error: "Unauthorized: Your token may be expired." };
              if (response.status === 429) return { error: "Rate limit exceeded. Try again later." };
              return { error: `HTTP ${response.status}` };
            }

            const contentType = response.headers.get("content-type");
            if (!contentType || !contentType.includes("application/json")) {
              console.error(`Invalid JSON response for ${id}:`, await response.text());
              return { error: "Invalid response format (Not JSON)" };
            }

            return await response.json();

          })
        );

        setPlaylists(responses);
      } catch (error) {
        console.error("Error fetching playlists:", error);
      }
    };

    console.log("Playlist IDs:", playlistIds);

    fetchPlaylists();
    setTimeout(() => fetchPlaylists(), 2000);
  }, [accessToken, playlistIds]);

目前仍然会触发以下错误:

Failed to load resource: the server responded with a status of 403 ()
Error fetching (the Spotify playlist id): 403

备注:内容来源于stack exchange,提问作者ishi782524

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:33:03