Nodejs:如何读取和遍历ServerResponse? (或者,如何读取客户端 URL 参数?)

Nodejs: How to read and traverse ServerResponse? (or, How to read client URL parameters?)

用户流程:

我们需要访问此代码。

我目前的做法是控制台记录返回的响应。

这是控制台记录的内容:

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_IDCLIENT_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

程序如下:

  1. 用户通过表单发送查询。然后,服务器检查它是否已授予应用程序使用 Spotify 的 API 的权限。如果没有,它会发送一条重定向消息。
  2. 用户使用 Spotify 的凭据进行身份验证。
  3. Spotify 通过 redirect_uri 参数将客户端重定向到站点引用。
  4. 我们获取 Spotify 提供的 code,并通过提供我们的 CLIENT_IDSECRET_ID.
  5. 来交换用户的凭据
  6. 我们将这些凭据存储在内存中。
  7. 我们将客户端重定向到主站点。

下次用户运行查询时,我们将拥有有效凭据,因此我们可以查询 Spotify 的 API 和 return 结果。