如何在Express中访问Axios响应?

5

我刚开始使用Express,对如何使用路由参数进行Axios请求并根据请求返回更改一些本地内容感到困惑。这是我目前所拥有的:

helpers.js

const axios = require('axios');
const {
  titleSuffix,
  organizationPath,
  varietyPath
} = require('./constants');

let organizationData = {};
let varietyData = {};

const Helpers = {

  fetchOrganization: (organizationID) => {
    axios.get(organizationPath + organizationID)
      .then( (response) => {
        //console.log(response);
        organizationData = response.data.data;
      })
      .catch( (error) => {
        //console.log(error);
      });
      return organizationData;
  },

  fetchVariety: (varietyID) => {
    axios.get(varietyPath + varietyID)
      .then( (response) => {
        //console.log(response);
        varietyData = response.data.data;
      })
      .catch( (error) => {
        //console.log(error);
      });
      return varietyData;
  },

  setOrgOpenGraphTags: (growerHash, res) => {
    Helpers.fetchOrganization(growerHash);
    res.locals.meta.og.title = organizationData.name + titleSuffix;
    console.log('Org = ' + organizationData.name);
  },

  setVarOpenGraphTags: (contextualId, res) => {
    Helpers.fetchVariety(contextualId);
    res.locals.meta.og.title = varietyData.name + titleSuffix;
    console.log('Var = ' + varietyData.name);
  }

};

module.exports = Helpers;

server.js

// Express
const express = require('express');
const app = express();

// Helpers
const {
  setOrgOpenGraphTags,
  setVarOpenGraphTags
} = require('./helpers');

// Organization
app.get(['/org/:growerHash/*', '/_org/:growerHash/*'], (req, res) => {
  setOrgOpenGraphTags(req.params.growerHash, res);
  res.render('org');
});

我相当确定我错过了一些小东西,但似乎无法根据Axios的响应更改以下本地内容:

res.locals.meta.og.title

根据我目前的所了解,如何在Express中正确地访问Axios的响应并更改本地变量? 我真的需要一个基于我提供的代码来回答。 目前,在我的开发环境中,请求有效,但在生产环境中返回“undefined”。 非常感谢您的帮助。


fetchOrganization是异步的,您没有等待它。 - zero298
@zero298在这里为您提供了全面的答案。我建议您仔细阅读并接受他的回答 - 您不太可能在此获得更多答案。 - Kirk Larkin
1个回答

12
我链接的重复问题,为什么在函数内修改变量后变量未被更改?-异步代码参考,讨论了编写异步代码意味着必须传播异步性的原因和方法。
您现在编写的代码没有传播异步性。 axios.get() 返回一个Promise。 除非所有依赖于该Promise解析的值的内容都等待Promise链解析完成,否则您将无法得到期望的结果。
请考虑我下面注释的代码:
const axios = require('axios');

const Helpers = {
  fetchOrganization: (organizationID) => {
    // axios.get() will return a Promise
    // You have to wait for the Promise to finish before
    // you can use any data that it produces
    // You must propogate the Proise of data up

    // You should return axios.get(...)
    axios.get(organizationPath + organizationID)
      .then((response) => {
        //console.log(response);
        organizationData = response.data.data;
      })
      .catch((error) => {
        //console.log(error);
      });
    // This won't be populated by the time you try to use it
    return organizationData;

    // Instead do
    return axios
      .get(organizationPath + organizationID)
      .then(response => {
        const organizationData = response.data.data;
        return organizationData
      })
      .catch(err => console.error(err));

    // Better yet, do
    /*
    return axios.get(organizationPath + organizationID)
        .then(res => response.data.data) // Return is implied
        .catch(err => console.error(err));
    */
  },

  setOrgOpenGraphTags: (growerHash, res) => {
    // Nothing is coming out of this function and you aren't waiting on it
    Helpers.fetchOrganization(growerHash);

    // Instead do
    return Helpers.fetchOrganization(growerHash)
      .then(org => {
        return org.name + titleSuffix;
      });

    //res.locals.meta.og.title = organizationData.name + titleSuffix;
    //console.log('Org = ' + organizationData.name);
  }
}

// Organization
app.get(['/org/:growerHash/*', '/_org/:growerHash/*'], (req, res) => {
  // Below, you are starting the async process
  // but you don't wait for the async to finish
  // you just immediately res.render()
  setOrgOpenGraphTags(req.params.growerHash, res);
  res.render('org');

  // Instead
  setOrgOpenGraphTags(req.params.growerHash, res)
    .then(orgTitle => {
      res.locals.meta.og.title = orgTitle;
      res.render('org');
    });
});

考虑到这一点,让我们考虑一个简化版的代码,它将等待 Promise 链解决:
// Let's boil your app down to it's core
const SOME_SUFFIX = "foobar";

// fetchOrganization
function getSomeData(id) {
  return axios
    .get(`http://www.example.com/things/${id}`)
    .then(thatThing => thatThing.nested.property.i.want)
    .catch(err => console.error(err));
}

// setOrgOpenGraphTags
function calculateDerivedData(id) {
  return getSomeData(id)
    .then(thatThingsProperty => `${thatThingsProperty}-${SOME_SUFFIX}`)
}

// Route
app.get("/some/endpoint/:id", (req, res) => {
  calculateDerivedData(req.params.id)
    .then(thatDerivedDataWeNeed => {
      res.locals.whatever = thatDerivedDataWeNeed;
      res.render("someTemplate");
    })
});

如果你想编写看起来更加简洁的代码,你也可以考虑使用async/await
// Let's boil your app down to it's core
const SOME_SUFFIX = "foobar";

// fetchOrganization
async function getSomeData(id) {
    try {
        const thatThing = await axios.get(`http://www.example.com/things/${id}`);
        return thatThing.nested.property.i.want;
    } catch(err){
        console.error(err);
    }
}

// setOrgOpenGraphTags
async function calculateDerivedData(id) {
    const thatThingsProperty = await getSomeData(id);
    return `${thatThingsProperty}-${SOME_SUFFIX}`;
}

// Route
app.get("/some/endpoint/:id", async function(req, res) => {
  res.locals.whatever = await calculateDerivedData(req.params.id);
  res.render("someTemplate");
});

嘿@zero298,感谢你花时间深入回答。这绝对有帮助,但我注意到在生产中返回了“bound consoleCall”。有任何想法是为什么吗?我以前从未遇到过这种情况。 - nunya
@nunya 它返回到哪里? - zero298
<meta property="og:title" content="bound consoleCall" /> - nunya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接