Nodejs:如何读取和遍历ServerResponse? (或者,如何读取客户端 URL 参数?)
Nodejs: How to read and traverse ServerResponse? (or, How to read client URL parameters?)
用户流程:
- 用户向服务器发出 post 请求。
- 该请求将用户重定向到 Spotify 的授权端点,在那里他授予对我们的网络应用程序的特定访问权限。
- 用户被重定向回我们的网站,地址栏中的 URL 现在已从
http://localhost:3000
更改为 http://localhost:3000/?code={code}
我们需要访问此代码。
我目前的做法是控制台记录返回的响应。
这是控制台记录的内容:
https://docs.google.com/document/d/1FII3_xrjb6lmTkma20TDjoyRXN_yKZ-knmFgFyfALKc/edit?usp=sharing
相同的控制台日志,没有编号:
https://docs.google.com/document/d/12qeVkp82opa2ITk0wGs8Fv0_Zej38OzUkJGRh8TzFfE/edit?usp=sharing
滚动到第 1259 行,您会发现:
params: {},
query:
{ code:
'AQC_G...nzDFS'
},
我们如何访问这个 'code' 的值?
在线解析为JSON(希望得到'query'的路径),提示“无法解析无效的JSON格式。”
此外,这是 app.js:
const express = require("express");
const https = require("https");
const bodyParser = require("body-parser");
const axios = require("axios");
const app = express();
app.listen(3000, function() {
})
// The page to load when the browser (client) makes request to GET something from the server on "/", i.e., from the homepage.
app.get("/", function(req, res) {
res.sendFile(__dirname + "/index.html");
console.log(res); // This is logging that long ServerResponse.
});
let authURL = "https://accounts.spotify.com/authorize?client_id={client_id}&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F&scope=user-read-playback-state%20app-remote-control%20user-modify-playback-state%20user-read-currently-playing%20user-read-playback-position%20user-read-email%20streaming"
// Redirect user to Spotify's endpoint.
app.post("/", function(req, res) {
res.redirect(authURL);
});
// The data that server should POST when the POST request is sent by the client, upon entering the search queryValue, in the search bar (form).
app.post("/", function(req, res) {
// The user input query. We are using body-parser package here.
const query = req.body.queryValue;
let searchUrl = "https://api.spotify.com/v1/search?q=" + query + "&type=track%2Calbum%2Cartist&limit=4&market=IN";
//Using Axios to fetch data. It gets parsed to JSON automatically.
axios.get(searchUrl, {
headers: {
'Authorization': token,
}
})
.then((resAxios) => {
console.log(resAxios.data)
//Extracting required data.
})
.catch((error) => {
console.error(error)
})
});
这是index.js:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Title</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="index-search-container">
<form class="" action="/" method="post">
<input id="queryId" type="text" name="queryValue" value="" placeholder="Search">
<button type="submit" name="button">HIT ME</button>
</form>
</div>
</body>
</html>
注意 index.js 中的 'POST' 方法 'form'。该表单实际上用于将 queryValue 发送到服务器,并返回一些从 API 获取的数据。 在app.js中,现在有两个app.post,只有第一个有效。请帮助我改进代码。 :)
您是否已将回调 URI 添加到 Spotify 应用程序?
如果是,则为该 URI 创建一个路由,您将能够访问 Spotify 返回的数据。
这是一个示例,您可以使用它来请求用户代表他们联系 Spotify 的 API:
const express = require("express")
const bodyParser = require("body-parser")
const cookieParser = require("cookie-parser")
const axios = require("axios")
const crypto = require("crypto")
/**
* Environment Variables
*/
const PORT = process.env.PORT || 3000
const PROTOCOL = process.env.PROTOCOL || "http"
const URL = process.env.URL || `${PROTOCOL}://localhost:${PORT}`
const SEARCH_URL = process.env.SEARCH_URL || "https://api.spotify.com/v1/search"
const AUTH_URL = process.env.AUTH_URL || "https://accounts.spotify.com/authorize"
const TOKEN_URL = process.env.TOKEN_URL || "https://accounts.spotify.com/api/token"
const SCOPE = process.env.SCOPE || "user-read-playback-state app-remote-control user-modify-playback-state user-read-currently-playing user-read-playback-position user-read-email streaming"
/**
* Spotify's Client Secrets
*/
const CLIENT_ID = process.env.CLIENT_ID
const CLIENT_SECRET = process.env.CLIENT_SECRET
/**
* Memory Store
*/
const DB = {}
/**
* Express App Configuration
*/
const app = express()
app.use(bodyParser.json())
app.use(cookieParser())
app.use((_, res, next) => {
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Headers', "*");
next()
})
// Home
app.get("/", (req, res) => {
res.status(200)
.header("Content-Type", "text/html")
.send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Spotify Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<form id="searchForm">
<input id="queryId" type="text" name="query" value="" placeholder="Search">
<button type="submit" name="button">HIT ME</button>
</form>
<pre id="results">
<script>
const form$ = document.getElementById("searchForm")
const results$ = document.getElementById("results")
form$.addEventListener("submit", (e) => {
e.preventDefault()
fetch("/", {
method: "POST",
credentials: "same-origin",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({query: e.target.elements.query.value})
})
.then((response) => {
if (response.status === 301) {
window.location.href = "/login"
return
}
return response.json()
})
.then((data) => results$.textContent = JSON.stringify(data, null, 2))
.catch((err) => console.error(err))
})
</script>
</body>
</html>
`)
})
// Search
app.post("/", (req, res) => {
const id = req.cookies.id
if (id === undefined || DB[id] === undefined) {
res.status(301).send()
return
}
axios({
url: `${SEARCH_URL}?q=${encodeURIComponent(req.body.query)}&type=track%2Calbum%2Cartist&limit=4&market=IN`,
method: "get",
headers: {"Authorization": `Bearer ${DB[id].access_token}`},
})
.then((response) => {
res.status(200).json(response.data)
})
.catch((err) => {
console.error(err)
res.status(400).send(err.message)
})
})
// Login
app.get("/login", (_, res) => {
res.redirect(`${AUTH_URL}?client_id=${CLIENT_ID}&scope=${encodeURIComponent(SCOPE)}&response_type=code&redirect_uri=${encodeURIComponent(URL + '/callback')}`)
})
// Callback
app.get("/callback", (req, res) => {
const id = crypto.randomBytes(9).toString("hex")
const code = req.query.code
axios({
url: TOKEN_URL,
method: "post",
data: Object.entries({grant_type: "client_credentials", code, redirect_uri: URL})
.map(([key, value]) => key + "=" + encodeURIComponent(value)).join("&"),
headers: {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
},
auth: {username: CLIENT_ID, password: CLIENT_SECRET},
})
.then((response) => {
DB[id] = response.data
res.cookie("id", id, {maxAge: response.data.expires_in * 1000, httpOnly: true, path: "/"}).redirect("/")
})
.catch((err) => {
console.error(err)
res.status(400).send(err.message)
})
})
/**
* Listen
*/
app.listen(PORT, () => console.log(`- Listening on port ${PORT}`))
您需要提供 CLIENT_ID
、CLIENT_SECRET
和重定向 URL
作为环境变量。我建议这样调用 index.js
脚本:
env CLIENT_ID=xxx \
env CLIENT_SECRET=zzz \
env URL=http://spotify.127.0.0.1.nip.io:3000 \
node index.js
程序如下:
- 用户通过表单发送查询。然后,服务器检查它是否已授予应用程序使用 Spotify 的 API 的权限。如果没有,它会发送一条重定向消息。
- 用户使用 Spotify 的凭据进行身份验证。
- Spotify 通过
redirect_uri
参数将客户端重定向到站点引用。
- 我们获取 Spotify 提供的
code
,并通过提供我们的 CLIENT_ID
和 SECRET_ID
. 来交换用户的凭据
- 我们将这些凭据存储在内存中。
- 我们将客户端重定向到主站点。
下次用户运行查询时,我们将拥有有效凭据,因此我们可以查询 Spotify 的 API 和 return 结果。
用户流程:
- 用户向服务器发出 post 请求。
- 该请求将用户重定向到 Spotify 的授权端点,在那里他授予对我们的网络应用程序的特定访问权限。
- 用户被重定向回我们的网站,地址栏中的 URL 现在已从
http://localhost:3000
更改为http://localhost:3000/?code={code}
我们需要访问此代码。
我目前的做法是控制台记录返回的响应。
这是控制台记录的内容:
https://docs.google.com/document/d/1FII3_xrjb6lmTkma20TDjoyRXN_yKZ-knmFgFyfALKc/edit?usp=sharing
相同的控制台日志,没有编号:
https://docs.google.com/document/d/12qeVkp82opa2ITk0wGs8Fv0_Zej38OzUkJGRh8TzFfE/edit?usp=sharing
滚动到第 1259 行,您会发现:
params: {},
query:
{ code:
'AQC_G...nzDFS'
},
我们如何访问这个 'code' 的值?
在线解析为JSON(希望得到'query'的路径),提示“无法解析无效的JSON格式。”
此外,这是 app.js:
const express = require("express");
const https = require("https");
const bodyParser = require("body-parser");
const axios = require("axios");
const app = express();
app.listen(3000, function() {
})
// The page to load when the browser (client) makes request to GET something from the server on "/", i.e., from the homepage.
app.get("/", function(req, res) {
res.sendFile(__dirname + "/index.html");
console.log(res); // This is logging that long ServerResponse.
});
let authURL = "https://accounts.spotify.com/authorize?client_id={client_id}&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F&scope=user-read-playback-state%20app-remote-control%20user-modify-playback-state%20user-read-currently-playing%20user-read-playback-position%20user-read-email%20streaming"
// Redirect user to Spotify's endpoint.
app.post("/", function(req, res) {
res.redirect(authURL);
});
// The data that server should POST when the POST request is sent by the client, upon entering the search queryValue, in the search bar (form).
app.post("/", function(req, res) {
// The user input query. We are using body-parser package here.
const query = req.body.queryValue;
let searchUrl = "https://api.spotify.com/v1/search?q=" + query + "&type=track%2Calbum%2Cartist&limit=4&market=IN";
//Using Axios to fetch data. It gets parsed to JSON automatically.
axios.get(searchUrl, {
headers: {
'Authorization': token,
}
})
.then((resAxios) => {
console.log(resAxios.data)
//Extracting required data.
})
.catch((error) => {
console.error(error)
})
});
这是index.js:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Title</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="index-search-container">
<form class="" action="/" method="post">
<input id="queryId" type="text" name="queryValue" value="" placeholder="Search">
<button type="submit" name="button">HIT ME</button>
</form>
</div>
</body>
</html>
注意 index.js 中的 'POST' 方法 'form'。该表单实际上用于将 queryValue 发送到服务器,并返回一些从 API 获取的数据。 在app.js中,现在有两个app.post,只有第一个有效。请帮助我改进代码。 :)
您是否已将回调 URI 添加到 Spotify 应用程序? 如果是,则为该 URI 创建一个路由,您将能够访问 Spotify 返回的数据。
这是一个示例,您可以使用它来请求用户代表他们联系 Spotify 的 API:
const express = require("express")
const bodyParser = require("body-parser")
const cookieParser = require("cookie-parser")
const axios = require("axios")
const crypto = require("crypto")
/**
* Environment Variables
*/
const PORT = process.env.PORT || 3000
const PROTOCOL = process.env.PROTOCOL || "http"
const URL = process.env.URL || `${PROTOCOL}://localhost:${PORT}`
const SEARCH_URL = process.env.SEARCH_URL || "https://api.spotify.com/v1/search"
const AUTH_URL = process.env.AUTH_URL || "https://accounts.spotify.com/authorize"
const TOKEN_URL = process.env.TOKEN_URL || "https://accounts.spotify.com/api/token"
const SCOPE = process.env.SCOPE || "user-read-playback-state app-remote-control user-modify-playback-state user-read-currently-playing user-read-playback-position user-read-email streaming"
/**
* Spotify's Client Secrets
*/
const CLIENT_ID = process.env.CLIENT_ID
const CLIENT_SECRET = process.env.CLIENT_SECRET
/**
* Memory Store
*/
const DB = {}
/**
* Express App Configuration
*/
const app = express()
app.use(bodyParser.json())
app.use(cookieParser())
app.use((_, res, next) => {
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Headers', "*");
next()
})
// Home
app.get("/", (req, res) => {
res.status(200)
.header("Content-Type", "text/html")
.send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Spotify Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<form id="searchForm">
<input id="queryId" type="text" name="query" value="" placeholder="Search">
<button type="submit" name="button">HIT ME</button>
</form>
<pre id="results">
<script>
const form$ = document.getElementById("searchForm")
const results$ = document.getElementById("results")
form$.addEventListener("submit", (e) => {
e.preventDefault()
fetch("/", {
method: "POST",
credentials: "same-origin",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({query: e.target.elements.query.value})
})
.then((response) => {
if (response.status === 301) {
window.location.href = "/login"
return
}
return response.json()
})
.then((data) => results$.textContent = JSON.stringify(data, null, 2))
.catch((err) => console.error(err))
})
</script>
</body>
</html>
`)
})
// Search
app.post("/", (req, res) => {
const id = req.cookies.id
if (id === undefined || DB[id] === undefined) {
res.status(301).send()
return
}
axios({
url: `${SEARCH_URL}?q=${encodeURIComponent(req.body.query)}&type=track%2Calbum%2Cartist&limit=4&market=IN`,
method: "get",
headers: {"Authorization": `Bearer ${DB[id].access_token}`},
})
.then((response) => {
res.status(200).json(response.data)
})
.catch((err) => {
console.error(err)
res.status(400).send(err.message)
})
})
// Login
app.get("/login", (_, res) => {
res.redirect(`${AUTH_URL}?client_id=${CLIENT_ID}&scope=${encodeURIComponent(SCOPE)}&response_type=code&redirect_uri=${encodeURIComponent(URL + '/callback')}`)
})
// Callback
app.get("/callback", (req, res) => {
const id = crypto.randomBytes(9).toString("hex")
const code = req.query.code
axios({
url: TOKEN_URL,
method: "post",
data: Object.entries({grant_type: "client_credentials", code, redirect_uri: URL})
.map(([key, value]) => key + "=" + encodeURIComponent(value)).join("&"),
headers: {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
},
auth: {username: CLIENT_ID, password: CLIENT_SECRET},
})
.then((response) => {
DB[id] = response.data
res.cookie("id", id, {maxAge: response.data.expires_in * 1000, httpOnly: true, path: "/"}).redirect("/")
})
.catch((err) => {
console.error(err)
res.status(400).send(err.message)
})
})
/**
* Listen
*/
app.listen(PORT, () => console.log(`- Listening on port ${PORT}`))
您需要提供 CLIENT_ID
、CLIENT_SECRET
和重定向 URL
作为环境变量。我建议这样调用 index.js
脚本:
env CLIENT_ID=xxx \
env CLIENT_SECRET=zzz \
env URL=http://spotify.127.0.0.1.nip.io:3000 \
node index.js
程序如下:
- 用户通过表单发送查询。然后,服务器检查它是否已授予应用程序使用 Spotify 的 API 的权限。如果没有,它会发送一条重定向消息。
- 用户使用 Spotify 的凭据进行身份验证。
- Spotify 通过
redirect_uri
参数将客户端重定向到站点引用。 - 我们获取 Spotify 提供的
code
,并通过提供我们的CLIENT_ID
和SECRET_ID
. 来交换用户的凭据
- 我们将这些凭据存储在内存中。
- 我们将客户端重定向到主站点。
下次用户运行查询时,我们将拥有有效凭据,因此我们可以查询 Spotify 的 API 和 return 结果。