【问题标题】:Express.js: res.render() does not send data when wrapped in promise chainExpress.js:res.render() 包裹在 Promise 链中时不发送数据
【发布时间】:2017-11-06 12:41:28
【问题描述】:

我最近开始重构旧的回调地狱路由以使用 Promise。 (注意:重构不完整,因此以下一些代码很难看)

这样做破坏了我的res.render() 功能。

  • 我正在为我的渲染引擎使用 EJS。
  • 我的大部分承诺链都是基于 MongoDB / Mongoose 查询构建的。

基本的应用程序结构是一个复杂的调查/测验,将用户分为不同的类别。它存储他们的信息并在最后呈现一个自定义的 EJS 页面。基本上,最终调用的工作原理如下:

         mongoose.model('Result')
             .findOne({quizId: req.query.id})
             .then(getPageData)
             .then(renderPage)
             .catch(errorHandler)

理论上很简单。

相关功能:

    let getPageData = (mongooseResult) => {

      let resultsPage;
      let analyticsData;

      if (mongooseResult.answers.section1.question1.answer != null && mongooseResult.answers.section1.question1.answer != undefined){
          let resultsPage = WP.getPage(wp_pages_data, 'slug', mongooseResult.coreFit);
          let analyticsData = {
            quizId: mongooseResult.quizId,
            coreFit: mongooseResult.coreFit,
            secondFit: mongooseResult.secondFit,
            degree: mongooseResult.answers.section1.question1.answer,
            year: mongooseResult.answers.section1.question2.answer,
            concentration: mongooseResult.answers.section1.question3.answer,
            views: mongooseResult.views,
          }

        mongooseResult.views++;
        mongooseResult.save();

        return [resultsPage, analyticsData, mongooseResult];
      } else {
        throw new Error('S1Q1 IS UNDEFINED');
      }
    };
    let renderPage = ([resultsPage, analyticsData, mongooseResult]) => {
      if (resultsPage != null && resultsPage != undefined){

        // WHY IS MY RENDER NOT WORKING???
        res.render('templates/' + mongooseResult.coreFit,  Object.assign({}, WP.get_WP_data(resultsPage), getPartials(mongooseResult.modules), analyticsData), (err, html) => {if (err) console.log(err); else res.send(html);});
      } else {
        throw new Error('GETTING PAGE DATA RETURNED UNDEFINED');
      }
    };

传入的请求首先被路由到/submit POST 路由 - 它处理一些数据,将其全部存储在数据库中,然后将res.redirect() 路由到运行上述逻辑的/results?id=12345 GET 路由。

有趣的是render() 实际触发了;回调 ((err, html) => {if (err) console.log(err); else res.send(html);}) 将呈现的 HTML 发送到如果您第二次点击路线。

完成调查不会产生回复;但它确实成功地将我们所有的信息存储在数据库中,并且控制台记录了客户端请求应重定向到的 ID。用这个生成的 ID / URL 访问 GET 路由,实际上是通过复制粘贴它,渲染得很好。

将渲染换成简单的res.send('wtf') 是可行的——实际的响应会发送到客户端。 (在本例中为字符串 'wtf' ...)

响应只是不发回数据。我不明白。有什么想法吗?

更新: 奇怪的是,手动访问路由与我之前的 POST 请求中的重定向通过简单的res.send('wtf mate') 产生不同的结果。

点击 GET 路由并手动将先前存在的 ID 传递给 req.query:

在来自先前 POST 请求的 res.redirect() 调用之后点击 GET 路由:

这可能更多是 Express 请求类型不匹配的问题?为什么res.redirect('/results?id=someString') 调用会改变 URL 而不是发回字符串/HTML?

【问题讨论】:

  • 为什么你将匿名函数作为最后一个参数传递? res.render 并将其发送给客户端。我认为没有必要做额外的res.send
  • 你是对的 - 在这种情况下仅用于调试;我想看看添加额外的回调是否有帮助 - 或者是否有一个秘密 err 在幕后被吞没。
  • 由于您使用mongooseResult.save() 之类的代码,您确定POST /submit 实际上等待 数据库保存/更新在重定向之前完成吗?

标签: node.js express mongoose promise ejs


【解决方案1】:

让我们尝试通过测试代码部分来调试问题。

我的线索很少,其中之一是'templates/' + result.coreFit 没有模板。
如果templates 文件夹中存在具有相同值名的文件,请尝试检查coreFit 值。

试试这段代码,告诉我它是否有效:

const 
  _ = require('lodash'),
  mongoose = require('mongoose');

const getQuizResultById = quizId => {
    return new Promise((resolve, reject) => {
        const 
            Result = mongoose.model('Result'),
            query = {
                quizId
            };

        Result
            .findOne(query)
            .then(resolve)
            .catch(reject);
    });
};

const getPageData = result => {
    if (_.get(result, 'answers.section1.question1.answer')) {
        let resultsPage = WP.getPage(wp_pages_data, 'slug', result.coreFit);
        let 
        analyticsData = _.pick(result, ['quizId', 'coreFit', 'secondFit', 'views']);
        analyticsData.degree = _.get(result, 'answers.section1.question1.answer');
        analyticsData.year = _.get(result, 'answers.section1.question2.answer');        
        analyticsData.concentration = _.get(result, 'answers.section1.question3.answer');

        result.views++;
        result.save();

        return [resultsPage, analyticsData, result];
    }

    throw new Error('S1Q1 IS UNDEFINED');
}

const renderPage = ([resultsPage, analyticsData, result], res) => {
    if (resultsPage) {
        return res.render(
            'templates/' + result.coreFit,  
            Object.assign({}, WP.get_WP_data(resultsPage), getPartials(result.modules), analyticsData), 
            (err, html) => {
                if (err) {
                    console.log(err);
                    return res.status(500).send('Ooopsss...');
                }
                res.send(html);
            });
    } 

    throw new Error('GETTING PAGE DATA RETURNED UNDEFINED');
};

router.get('/results', (req, res) => {
    getQuizResultById(req.query.id)
        .then(getPageData)
        .then(data => {
           renderPage(data, res);
        })
        .catch(errorHandler);
});

【讨论】:

  • 感谢@num8er 的回复!事实证明,在重构过程中,这是我在客户端方面的一次糟糕的疏忽。看看上面 Lars 的回答——关键的线索是我的 URL 栏在提交最终调查时作为重定向的一部分在点击 GET 路由时是如何重新路由的……因为 99% 的重构是服务器,所以这块完全间隔了。侧面
【解决方案2】:

根据您的更新,听起来您的 res.send() 呼叫在客户端被奇怪地解释了。 POST 请求不应影响 URL 栏,除非您明确告知它 - 您是否检查过客户端最初发送此请求的位置?

我的猜测-因为您说您上面发布的路由代码是在先前的 POST 路由上从 res.redirect() 命中的-是有客户端逻辑期望 response 对象是 URL 字符串,并且通过您的重定向首次访问 GET 路由会传递此响应对象。这样客户端就会尝试在 URL 栏中插入大量 HTML 字符串。

在这种情况下,手动点击 GET 路由,就像你说的那样,默认情况下只会从 res.render() 呈现 HTML - 但你从 res.redirect() 的初始点击可能不会。

【讨论】:

  • 卫生署!你搞定了......这是我的客户端 AJAX 的一部分:success: (res) => { window.location = res; } 所以,是的,你是对的。由于res.render() 返回一个HTML 字符串,所以URL 栏不知道如何处理它。但默认的 GET 行为需要 HTML - 因此手动 ping 路由行为正确。我没有做res.redirect(),而是做了一个res.send('results?id=12345')——它将客户端重定向到我的GET路由,这一切都很好!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-16
  • 1970-01-01
  • 2014-09-13
  • 1970-01-01
  • 2019-05-05
  • 1970-01-01
  • 2021-02-28
相关资源
最近更新 更多