【问题标题】:Axios requests with express, node, ejs带有 express、node、ejs 的 axios 请求
【发布时间】:2020-01-23 18:23:19
【问题描述】:

我正在使用 Express.js、node.js、Axios 和 ejs 开发一个网站。我正在使用 Axios 对 Oracle SQL REST 服务进行 REST 调用。我在使用 Promises 或 Async/Await 时遇到问题。如果可能的话,我可以使用一些指导。

我有一个与 Oracle DB 接口的存储库层。例如:

dataaccess.js

const axios = require('axios');

exports.IsManufacturerCategory = function (categoryId) {
    axios.get(`DB ADDRESS ${categoryId}`)
        .then(response => {
            console.error('GET IsManufacturerCategory categoryId = ' + categoryId);
            console.error('Response = ' + JSON.stringify(response.data));

            return (response.data);
        })
        .catch(rej => {
            console.error('ERROR IsManufacturerCategory categoryId = ' + categoryId);
            console.error('ERR = \n' + rej.data);
            return (rej.data);
        });
}

在我的中间件中调用。当我打电话给var isManufacturerCat = exports.IsManufacturerCategory(categoryId); 时,它是未定义的。我正在尝试使用从 Axios 调用中检索到的数据将 ejs 视图返回到我的路由器,如果需要,我可以提供该视图。

category.js

var isManufacturerCat = exports.IsManufacturerCategory(categoryId);
if (isManufacturerCat) {
    var models = dataaccess.GetCategorySubCategories(categoryId);
    return ("manufacturers", {
        data: {
            Canonical: cononical, 
            Category: category, 
            IsAManufacturerCategory: iAManufacturerCat, 
            Models: models
        }
    });
}

我对我的项目结构、Promises 的使用、Async/Await 等方面的任何建议持开放态度。

提前谢谢你。

编辑

在处理了一些给出的答案后,我取得了一些进展,但我遇到了异步调用层的问题。我最终进入了一个需要await 呼叫的位置,但我处于一个我不能/不想这样做的功能中(即我的路由器)。

indexMiddleware.js

exports.getRedirectURL = async function (fullOrigionalpath) {

    if (fullOrigionalpath.split('.').length == 1 || fullOrigionalpath.indexOf(".aspx") != -1) {
        if (fullOrigionalpath.indexOf(".aspx") != -1) { 
            //some string stuff to get url

        }
        else if (fullOrigionalpath.indexOf("/solutions/") != -1) {
            if (!fullOrigionalpath.match("/solutions/$")) {
                if (fullOrigionalpath.indexOf("/t-") != -1) {
                    //some stuff
                }
                else {
                    var solPart = fullOrigionalpath.split("/solutions/");
                    solPart = solPart.filter(function (e) { return e });

                    if (solPart.length > 0) {
                        var solParts = solPart[solPart.length - 1].split("/");
                        solParts = solParts.filter(function (e) { return e });
                        if (solParts.length == 1) {
                            waitForRespose = true;

                            const isASolutionCategory = await dataaccess.isASolutionCategory(solParts[0]); // returns void

                            if (isASolutionCategory != undefined && isASolutionCategory.length > 0 && isASolutionCategory[0].Count == 1) {
                                 // set redirecturl   
                            }
                        }
                        else {
                            redirecturl = "/solutions/solutiontemplate";
                        }
                    }
                }
            }
        }
        else if (URL STUFF) {
            // finally if none of the above fit into url condition then verify current url with Category URL or product url into database and if that matches then redirect to proper internal URL
            if (fullOrigionalpath.lastIndexOf('/') == (fullOrigionalpath.length - 1)) {
                fullOrigionalpath = fullOrigionalpath.substring(0, fullOrigionalpath.lastIndexOf('/'));
            }

            waitForRespose = true;

            const originalURL = await exports.getOriginalUrl(fullOrigionalpath); //returns string
            redirecturl = originalURL;
            return redirecturl;
        }
    }

    if (!waitForRespose) {
        return redirecturl; 
    }
}

exports.getOriginalUrl = async function (friendlyUrl) {
    var originalUrl = '';
    var urlParts = friendlyUrl.split('/');
    urlParts = urlParts.filter(function (e) { return e });
    if (urlParts.length > 0) {
        var skuID = urlParts[urlParts.length - 1];

        const parts = await dataaccess.getFriendlyUrlParts(skuID); //returns void
        console.log("Inside GetOriginalUrl (index.js middleware) FriendlyUrlParts: " + parts);//undefined

        if (parts != undefined && parts != null && parts.length > 0) {
            //some stuff
        }
        else {
            // verify whether it's category URL then return the category local URL
            console.log('Getting CategoryLocalUrl');
            const categoryLocalUrl = await dataaccess.getCategoryLocalUrl(friendlyUrl); // returns void
            console.log('CategoryLocalUrl Gotten ' + JSON.stringify(categoryLocalUrl)); //undefined
            if (categoryLocalUrl != undefined && categoryLocalUrl.length > 0) {
                //set originalUrl
                return originalUrl;
            }

        }
    }
    else { return ''; }
}

index.js 路由器

router.use(function (req, res, next) {
   //bunch of stuff
   index.getRedirectURL(url)
      .then(res => {
        req.url = res;
      })
      .catch(error => {
        console.error(error);
      })
      .finally(final => {
        next();
      });
}

awaits 之后,我在console.logs 中收到undefined。我猜我不太确定自己在做什么。

【问题讨论】:

    标签: javascript node.js express axios


    【解决方案1】:

    让我们从 dataaccess.js 开始。基本上,您正在导出一个正在执行异步工作的函数,但该函数不是异步的。大多数人都希望能够使用 async/await,因此与其让 IsManufacturerCategory 接受回调函数,不如让函数返回一个 Promise。最简单的方法是使函数成为异步函数。异步函数返回比返回显式承诺更容易解决/拒绝的承诺。以下是可以重写的方法:

    const axios = require('axios');
    
    exports.IsManufacturerCategory = async function (categoryId) {
      try {
        const response = await axios.get(`DB ADDRESS ${categoryId}`);
    
        console.log('GET IsManufacturerCategory categoryId = ' + categoryId);
        console.log('Response = ' + JSON.stringify(response.data));
      } catch (err) {
        console.error('ERROR IsManufacturerCategory categoryId = ' + categoryId);
        console.error('ERR = \n' + rej.data);
    
        throw err;
      }
    }
    

    请注意,我使用console.error 仅用于错误。因为您想记录一些与错误相关的详细信息,所以我使用了 try/catch 语句。如果您不关心这样做,该功能可以简化为:

    const axios = require('axios');
    
    exports.IsManufacturerCategory = async function (categoryId) {
        const response = await axios.get(`DB ADDRESS ${categoryId}`);
    
        console.log('GET IsManufacturerCategory categoryId = ' + categoryId);
        console.log('Response = ' + JSON.stringify(response.data));
    }
    

    这是因为异步函数会自动吞下错误并将其视为拒绝 - 所以IsManufacturerCategory 返回的承诺将被拒绝。同样,当异步函数返回一个值时,promise 会使用返回的值来解析。

    继续使用 category.js... 这看起来很奇怪,因为我认为您是从该模块的导出中访问 IsManufacturerCategory,而我认为您的意思是从数据访问模块的导入中访问它,对吧?

    在这个函数中,你应该把任何异步工作放在一个异步函数中,这样你就可以将 await 与返回 Promise 的函数一起使用。以下是它的重写方式:

    const dataaccess = require('dataccess.js');
    
    async function validateManufacturerCat(categoryId) {
      const isManufacturerCat = await dataaccess.IsManufacturerCategory(categoryId);
    
      if (isManufacturerCat) {
        const models = await dataaccess.GetCategorySubCategories(categoryId);
    
        return ({
          manufacturers: {
            data: {
              Canonical: cononical, 
              Category: category, 
              IsAManufacturerCategory: iAManufacturerCat, 
              Models: models
            }
          }
        });
      }
    }
    
    validateManufacturerCat(categoryId)
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.error(err);
      });
    

    几个注意事项:

    1. 我将if 语句中的返回值更改为单个值。在使用 Promise 和 async/await 时,您应该尝试始终返回一个值(因为您只能解析/返回一个值)。
    2. 我看到很多以大写字母开头的函数。 JavaScript 中有一个约定,其中带有大写字母的函数是构造函数(意味着使用 new 关键字调用)。

    【讨论】:

    • 我已经考虑到这一点并更新了我的代码。至于大写字母的函数,我继承了一个充满这些函数的代码库,以及回调地狱。我正在尝试将其转换为比回调更好的东西。
    • 我听到了。它可能是在 promise 还没有出现之前的代码库,回调地狱更是常态——并不是每个人都知道 Async 库。这些天不需要那么多,但它仍然很方便。如果您还有其他问题,请告诉我!
    • 我会在哪里调用返回所有分层承诺结果的最终承诺?我的路由器的router.use 函数或路由本身需要这些信息。出于某种原因,我觉得在路由器中调用 promise 并不是一个好习惯。这是一个公平的假设吗?
    • 我在更改代码时遗漏了一件事情,并且在绝对基本的编程知识方面有所失误,那就是 return response.data 函数内部的 return response.data 调用。我添加了这个并返回了数据。我想有时你只是忘记了基础知识。
    • 嗨 Josh,关于路由器,Express 目前不适合使用 Promise。 Valeri Karpov 在这里讨论了这个问题:thecodebarbarian.com/… 他使用包装器作为解决方法,但使用了一个简单的 try/catch 块。请参阅我编写的教程的这一部分:jsao.io/2018/04/creating-a-rest-api-handling-get-requests 请注意,我正在捕获逻辑中引发的任何异常并将错误传递给next,这正是 Express 所期望的。
    【解决方案2】:

    这是一个工作示例。请注意,当数据可以从 Promise 中获取时,您需要一个回调函数来获取数据

    //client.js
    const axios = require("axios")
    
    
        exports.IsManufacturerCategory = function (url, callback) { 
           axios.get(url)
             .then(response=>
                {
                     callback(response.data);
                })
                .catch(error=>{
                    callback( error);
                })
        };
    
    
        //app.js
       const client = require('./client');
    
        function process(data) {
    
            console.log(data)
        }    
         client.IsManufacturerCategory("http://localhost:3000/persons",process);
    

    documentation

    【讨论】:

    • 那么我会删除var query = 并使用 then 和 catch 吗?
    • 我已经更新了答案。我已经对你的代码进行了布局。你也应该从函数中返回数据
    • 当我使用var IsAManufacturerCategory = dataaccess.IsManufacturerCategory(categoryId);时仍然得到一个未定义的值
    • 我已经更新了代码。您需要一个回调函数来获取数据。我所做的示例按预期工作。
    • 好的,所以我更改了我的 Axios 调用,现在数据正在返回,但中间件在设置 IsAManufacturerCategory 变量之前没有等待调用返回。这就是我测试时导致未定义的原因。我能做些什么让它等待吗?感谢您的帮助
    猜你喜欢
    • 1970-01-01
    • 2017-12-08
    • 2014-12-29
    • 2020-03-16
    • 2021-06-16
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 2020-06-16
    相关资源
    最近更新 更多