【发布时间】: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