我对这个错误感到沮丧: main.js:86 GET https://api.spotify.com/v1/me/top/artists net::ERR_ABORTED 403 (Forbidden).

我的JS代码如下所示:

function getTopArtists(accessToken) {
  return fetch("https://api.spotify.com/v1/me/top/artists", {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  });
}
async function displayTopArtistsInConsole(profile) {
  const accessToken = ACCESS_TOKEN;

  getTopArtists(accessToken)
    .then((response) => {
      if (response.ok) {
        return response.json();
      } else {
        throw new Error("Failed to fetch top artists");
      }
    })
    .then((data) => {
      console.log("Top Artists:", data.items);
    })
    .catch((error) => {
      console.error(error);
    });
}

我试着调试我的代码,但我真的找不到任何解决我的问题的方法,也没有阅读所有关于Spotify API的文档.我确信我的令牌根本没有过期,因为它可以持续长达1小时,而我是在打开我的HTML页面的时刻运行代码的.这会不会与async这个关键字有关?我可以在这里提供完整的代码,提前感谢:

const clientId = "50a6862feb5a4139af099a0ca99e267a";
const params = new URLSearchParams(window.location.search);
const code = params.get("code");
let ACCESS_TOKEN = null;
if (!code) {
  document.getElementById("sign-in").addEventListener("click", () => {
    redirectToAuthCodeFlow(clientId);
  });
} else {
  const accessToken = await getAccessToken(clientId, code).then();
  // fetch("https://api.spotify.com/v1/me/top/tracks").then((res) => console.log(res));
  const profile = await fetchProfile(accessToken);
  populateUI(profile);
}

function generateCodeVerifier(length) {
  let text = "";
  let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

  for (let i = 0; i < length; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
}

async function generateCodeChallenge(codeVerifier) {
  const data = new TextEncoder().encode(codeVerifier);
  const digest = await window.crypto.subtle.digest("SHA-256", data);
  return btoa(String.fromCharCode.apply(null, [...new Uint8Array(digest)]))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

export async function redirectToAuthCodeFlow(clientId) {
  const verifier = generateCodeVerifier(128);
  const challenge = await generateCodeChallenge(verifier);

  localStorage.setItem("verifier", verifier);

  const params = new URLSearchParams();
  params.append("client_id", clientId);
  params.append("response_type", "code");
  params.append("redirect_uri", "http://localhost:5173/callback");
  params.append("scope", "user-read-private user-read-email");
  params.append("code_challenge_method", "S256");
  params.append("code_challenge", challenge);

  document.location = `https://accounts.spotify.com/authorize?${params.toString()}`;
}

export async function getAccessToken(clientId, code) {
  const verifier = localStorage.getItem("verifier");

  const params = new URLSearchParams();
  params.append("client_id", clientId);
  params.append("grant_type", "authorization_code");
  params.append("code", code);
  params.append("redirect_uri", "http://localhost:5173/callback");
  params.append("code_verifier", verifier);

  const result = await fetch("https://accounts.spotify.com/api/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: params,
  });

  const { access_token } = await result.json();

  ACCESS_TOKEN = access_token;
  return access_token;
}

async function fetchProfile(token) {
  const result = await fetch("https://api.spotify.com/v1/me", {
    method: "GET",
    headers: { Authorization: `Bearer ${token}` },
  });

  return await result.json();
}

function getTopArtists(accessToken) {
  return fetch("https://api.spotify.com/v1/me/top/artists", {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  });
}
async function displayTopArtistsInConsole(profile) {
  const accessToken = ACCESS_TOKEN;

  getTopArtists(accessToken)
    .then((response) => {
      if (response.ok) {
        return response.json();
      } else {
        throw new Error("Failed to fetch top artists");
      }
    })
    .then((data) => {
      console.log("Top Artists:", data.items);
    })
    .catch((error) => {
      console.error(error);
    });
}

function populateUI(profile) {
  console.log(profile);

  const profileSection = document.createElement("section");
  profileSection.setAttribute("id", "profile");

  const h2 = document.createElement("h2");
  h2.innerHTML = 'Il tuo profilo <span id="displayName"></span>';
  profileSection.appendChild(h2);

  const avatarSpan = document.createElement("span");
  avatarSpan.setAttribute("id", "avatar");
  profileSection.appendChild(avatarSpan);

  const ul = document.createElement("ul");
  ul.id = "lista";

  const profileDataElements = [
    { label: "User ID", id: "id" },
    { label: "Email", id: "email" },
    { label: "Spotify URI", id: "uri", isLink: true },
    { label: "Link", id: "url", isLink: true },
    { label: "Profile Image", id: "imgUrl" },
  ];

  profileDataElements.forEach((data) => {
    const li = document.createElement("li");
    const span = document.createElement("span");
    span.setAttribute("id", data.id);
    li.textContent = `${data.label}: `;

    if (data.isLink) {
      const link = document.createElement("a");
      link.setAttribute("id", data.id);
      link.setAttribute("href", "#");
      li.appendChild(link);
    } else {
      li.appendChild(span);
    }

    ul.appendChild(li);
  });

  profileSection.appendChild(ul);

  document.body.appendChild(profileSection);

  document.getElementById("displayName").innerText = profile.display_name;
  if (profile.images[0]) {
    const profileImage = new Image(200, 200);
    profileImage.src = profile.images[0].url;
    document.getElementById("avatar").appendChild(profileImage);
    document.getElementById("imgUrl").innerText = profile.images[0].url;
  }
  document.getElementById("id").innerText = profile.id;
  document.getElementById("email").innerText = profile.email;
  document.getElementById("uri").innerText = profile.uri;
  document.getElementById("uri").setAttribute("href", profile.external_urls.spotify);
  document.getElementById("url").innerText = profile.href;
  document.getElementById("url").setAttribute("href", profile.href);

  document.getElementById("profile").style.display = "block";

  document.getElementById("nota").remove();
  document.getElementById("nota").remove();
  document.getElementById("sign-in").remove();
  displayTopArtistsInConsole(profile);
}

document.addEventListener("DOMContentLoaded", async function () {
  const params = new URLSearchParams(window.location.search);
  const code = params.get("code");

  if (code) {
    const accessToken = await getAccessToken(clientId, code);
    const profile = await fetchProfile(accessToken);
    populateUI(profile);
  }
});

推荐答案

您已请求授权身份验证服务颁发的令牌用于作用域user-read-privateuser-read-email.

如果您查看the documentation for /me/top/{type},您可能会注意到需要使用作用域user-top-read来检索此信息.

您需要在方法redirectToAuthCodeFlow中请求此作用域作为对/authorize的调用的一部分:

params.append("scope", "user-read-private user-read-email user-top-read");

Javascript相关问答推荐

模块与独立组件

传递一个大对象以在Express布局中呈现

如何从隐藏/显示中删除其中一个点击?

JSDoc创建并从另一个文件导入类型

XSLT处理器未运行

如何将多维数组插入到另一个多维数组中?

当id匹配时对属性值求和并使用JavaScript返回结果

当输入字段无效时,我的应用程序不会返回错误

确保函数签名中的类型安全:匹配值

如何在不影响隐式类型的情况下将类型分配给对象?

变量在导入到Vite中的另一个js文件时成为常量.

在SHINY R中的嵌套模块中,不能使用Java代码

当标题被点击时,如何使内容出现在另一个div上?

图表4-堆叠线和条形图之间的填充区域

使用RxJS from Event和@ViewChild vs KeyUp事件和RxJS主题更改输入字段值

如何找到带有特定文本和测试ID的div?

AG-GRIDreact 显示布尔值而不是复选框

在Java脚本中构建接口的对象

在使用JavaScript以HTML格式显示Google Drive中的图像时遇到问题

每隔一行文本段落进行镜像(Boustrophedon)