【问题标题】:Implementing Spotify's authorization flow using NextJS's api routes throws cors error使用 NextJS 的 api 路由实现 Spotify 的授权流程会引发 cors 错误
【发布时间】:2021-11-07 04:34:51
【问题描述】:

我正在尝试在 NextJS 中创建登录流程,如此处所述:Spotify SDK API Tutorial,使用 NextJS 的 api 路由

我创建了两个处理程序:api/login.ts 和 api/callback.ts。

login.ts 处理调用 spotify 的授权端点所需的参数的构造。 callback.ts 处理来自授权服务器的重定向 url。它解析请求并发回数据。

当我访问路径:localhost:3000/api/login 时,它会通过响应很好地解析并在响应中向我发送所需的access_token、refresh_token。响应在浏览器中显示为 JSON。

现在,当我在组件中调用此端点时,问题就出现了。我在那里获取“/api/login”端点(请参阅:Login.tsx),但它会引发以下cors 错误:

Access to fetch at 'https://accounts.spotify.com/authorize/?response_type=code&client_id=woops&scope=streaming+user-read-email+user-read-private&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fcallback&state=woops' (redirected from 'http://localhost:3000/api/login') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

login.ts:

import type { NextApiRequest, NextApiResponse } from 'next'

const generateRandomString = (length: number): string => {
  let text = '';
  const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

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

const scope = "streaming \
user-read-email \
user-read-private";

export default async function login(
  req: NextApiRequest,
  res: NextApiResponse
) {
  
  const state = generateRandomString(16);

  const input = {
    response_type: "code",
    client_id: process.env.SPOTIFY_CLIENT_ID,
    scope,
    redirect_uri: "http://localhost:3000",
    state,
  } as Record<string, string>;

  const auth_query_parameters = new URLSearchParams(input)
  res.redirect(`https://accounts.spotify.com/authorize/?${auth_query_parameters.toString()}`);
}

callback.ts:

import type { NextApiRequest, NextApiResponse } from 'next'

const formBody = (details: Record<string, string>): string => {
  return Object.keys(details)
    .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(details[key]))
    .join('&')
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { code } = req.query;
  
  const spotifyURL = 'https://accounts.spotify.com/api/token';

  const formData = {
    code: code,
    redirect_uri: "http://localhost:3000/api/callback",
    grant_type: 'authorization_code'
  } as Record<string, string>;

  const config = {
    method: 'POST',
    headers: {
      'Authorization': 'Basic ' + (Buffer.from(process.env.SPOTIFY_CLIENT_ID + ':' + process.env.SPOTIFY_CLIENT_SECRET).toString('base64')),
      'Content-Type' : 'application/x-www-form-urlencoded'
    },
    body: formBody(formData),
    JSON: true
  };

  fetch(spotifyURL, config as RequestInit)
    .then(response => response.json())
    .then(data => {
      res
        .send(data)
    })
}

Login.tsx

import { useEffect, useState } from "react";
import Login from '../components/Login';
import WebPlayback from '../components/WebPlayback';

const useLogin = () => {
  const [token, setToken] = useState('');
  
  useEffect(() => {
    async function getToken() {
      const response = await fetch('/api/login', { mode: 'cors' });
      const json = await response.json();
      setToken(json.access_token)
    }
    getToken();
  }, []);

  return token;
}

const App = () => {
  const token = useLogin();
  return (
    <>
      {
        token === ''
        ? <Login />
        : <WebPlayback token={token} />
      }
    </>
  )
}

export default App;

【问题讨论】:

    标签: node.js reactjs typescript redirect next.js


    【解决方案1】:

    将您的模式设置为no-cors

    const response = await fetch('/api/login', { mode: 'no-cors' });

    【讨论】:

      猜你喜欢
      • 2018-08-12
      • 2017-12-23
      • 2018-09-22
      • 2019-09-12
      • 2021-03-08
      • 2021-04-02
      • 2014-11-20
      • 2019-05-03
      • 2021-07-16
      相关资源
      最近更新 更多