Express / NodeJS针对同一路由的多个mysql查询

时间:2018-04-15 21:48:18

标签: node.js express

在express / nodejs中创建的博客中,我试图在单篇文章页面中显示文章(正常工作)和一组2篇推荐文章。不幸的是,你可以在代码的注释位置看到它不起作用(不能两次渲染相同的模板)

在这种情况下,最好的方法是什么?

<!-- language: lang-js -->
router.get('/*', function(req, res, next) {

var slug=req.url.replace(/^\//,'').replace(/\/$/,'');
var bg = getRandomInt(5);

if(slug==''){
    connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', function (error, results, fields) {
res.render('index', { title: title, year: year, bg:bg, pages: results });
    });

}else{
    connection.query('SELECT * FROM `pages` where slug=?', [slug], function (error, results, fields)
             {
        if(results.length){ 

            res.render('page', { title: results[0].title, page:results[0] });
        }else{
            console.log(req);
            res.render('error',{url: 'http://'+req.headers.host+req.url});
        }
    });
    /* not working apparently you can't send the header of the template twice umhh
    connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2',  function (error, random, fields)
             {
                res.render('page', { pages:random}); 

             });
    */

}
 });

2 个答案:

答案 0 :(得分:1)

您无法将网页呈现两次,否则您将获得Error: Can't set headers after they are sent to the client

您需要做的是获取当前文章和推荐的页面,并在获得两个查询的结果后呈现页面。

为了实现我使用的Promise.all,然后执行了单res.render

router.get('/*', async (req, res, next) => {

    const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
    const bg = getRandomInt(5);

    if (slug == '') {
        const results = await query('SELECT * FROM `pages` WHERE NOT slug = "about"');

        return res.render('index', {
            title: title,
            year: year,
            bg: bg,
            pages: results
        });

    }

    // Get current article and recommended pages
    // Promise.all returns an array where each entry has
    // the resolved value of the promise passed at that index
    const [article, recommended] = await Promise.all([
        query('SELECT * FROM `pages` where slug=?', [slug]),
        query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2')
    ]);


    if (article.length) {

        // Render the article & recommended pages at once.
        res.render('page', {
            title: article[0].title,
            page: article[0],
            pages: recommended 
        });

    } else {
        console.log(req);
        res.render('error', {
            url: 'http://' + req.headers.host + req.url
        });
    }

});

// Query helper
// You can use promisify...
function query(statement, placeholders = []) {

    return new Promise((resolve, reject) => {

        connection.query(query, placeholders, (error, results) => {

            if(err)
                return reject(err);

            resolve(results);

        });
    });

}

答案 1 :(得分:1)

你现在的方式

  • 两个查询都将在不相关的时间完成(并调用他们的回调)
  • res.render将被多次调用,这不起作用,因为它假设所有数据都是在一次调用中发送的。所以它发送的HTTP标头不能发送两次。

根据您的意图更新。请注意,这使得查询的顺序顺序,这可能是不可取的。您将希望使用async lib来帮助管理它们同时运行它们并仍然合并结果:

router.get('/*', (req, res, next) => {
  const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
  const bg = getRandomInt(5);

  if (slug == '') {
    return connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', (error, results, fields) => {
      res.render('index', { title: title, year: year, bg: bg, pages: results });
    });
  } else {
    return connection.query('SELECT * FROM `pages` where slug=?', [slug], (error, results, fields) => {
      if (results.length) {
        return connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2', (error, random, fields) => {
          if (error) {
            // handle error
          }
          // consolidate renders into a single call
          // adjust the template file accordingly
          return res.render('page', { title: results[0].title, page: results[0], pages: random });
        });
      } else {
        console.log(req);
        return res.render('error', { url: 'http://' + req.headers.host + req.url });
      }
    });
  }
});

或者,考虑使用bluebird&amp; async / await,这只是另一种风格 - 为您提供基于节点8+的新选项。在这一次中,查询将再次同时启动。

const bluebird = require('bluebird');

router.get('/*', async (req, res, next) => {
  try {
    const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
    const bg = getRandomInt(5);
    if (slug == '') {
      const results = await bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', cb));
      return res.render('index', { title: title, year: year, bg: bg, pages: results });
    } else {
      const [results, random] = await Promise.all([
        bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` where slug=?', [slug], cb)),
        bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2', cb))
      ]);
      if (results && results.length) {
        return res.render('page', { title: results[0].title, page: results[0], pages: random });
      } else {
        return res.render('error', { url: 'http://' + req.headers.host + req.url });
      }
    }
  } catch (e) {
    return res.render('error', { url: 'http://' + req.headers.host + req.url });
  }
});