繁体   English   中英

将多个DB / mongoose查询的结果呈现给express.js中的视图

[英]rendering results of multiple DB/mongoose queries to a view in express.js

鉴于mongoose(或sequelize,或redis)查询的异步性质,当你在渲染视图之前需要进行多次查询时,你会怎么做?

例如,您在会话中有user_id ,并希望通过findOne检索有关该特定用户的一些信息。 但您还希望显示最近登录用户的列表。

exports.index = function (req, res) {
    var current_user = null

    Player.find({last_logged_in : today()}).exec(function(err, players) {
        if (err) return res.render('500');

        if (req.session.user_id) {
            Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
                if (err) return;
                if (player) {
                    current_user = player
                }
            })
        }

        // here, current_user isn't populated until the callback fires 
        res.render('game/index', { title: 'Battle!',
                   players: players,
                   game_is_full: (players.length >= 6),
                   current_user: current_user
        });
    });
};

所以res.render在第一个查询回调中,很好。 但是等待来自findOne的响应,看看我们是否知道这个用户呢? 它只是有条件地调用,所以我不能将render放在内部回调中,除非我为任何一个条件复制它。 不漂亮。

我可以想到一些解决方法 -

  • 使它真的异步并在客户端使用AJAX来获取当前用户的个人资料。 但这似乎比它的价值更多的工作。

  • 使用Q和promises在渲染之前等待findOne查询的解析。 但在某种程度上,这就像强制阻止使响应等待我的操作。 似乎不对。

  • 使用中间件功能获取当前用户信息。 这看起来更干净,使查询可重用。 但是,我不确定如何去做,或者它是否仍会出现同样的问题。

当然,在一个更极端的情况下,如果你有十几个查询要做,事情可能会变得丑陋。 那么,给定这种要求的通常模式是什么?

是的,这是异步代码中特别烦人的情况。 你可以做的是把你必须复制的代码放到一个本地函数中,以保持干燥:

exports.index = function (req, res) {
    var current_user = null

    Player.find({last_logged_in : today()}).exec(function(err, players) {
        if (err) return res.render('500');

        function render() {
            res.render('game/index', { title: 'Battle!',
                       players: players,
                       game_is_full: (players.length >= 6),
                       current_user: current_user
            });
        }

        if (req.session.user_id) {
            Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
                if (err) return;
                if (player) {
                    current_user = player
                }
                render();
            })
        } else {
            render();
        }
    });
};

但是,看看你在这里做了什么,你可能需要在多个请求处理程序中查找当前的播放器信息,所以在这种情况下你最好使用中间件。

就像是:

exports.loadUser = function (req, res, next) {
    if (req.session.user_id) {
        Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
            if (err) return;
            if (player) {
                req.player = player
            }
            next();
        })
    } else {
        next();
    }
}

然后你可以配置你的路由来调用loadUser只要你需要req.player填充,路由处理程序就可以直接从那里拉出玩家详细信息。

router.get("/",function(req,res){
    var locals = {};
    var userId = req.params.userId;
    async.parallel([
        //Load user Data
        function(callback) {
             mongoOp.User.find({},function(err,user){
                if (err) return callback(err);
                locals.user = user;
                callback();
            });
        },
        //Load posts Data
        function(callback) {
                mongoOp.Post.find({},function(err,posts){
               if (err) return callback(err);
                locals.posts = posts;
                callback();
            });
        }
    ], function(err) { //This function gets called after the two tasks have called their "task callbacks"
        if (err) return next(err); //If an error occurred, we let express handle it by calling the `next` function
        //Here `locals` will be an object with `user` and `posts` keys
        //Example: `locals = {user: ..., posts: [...]}`
         res.render('index.ejs', {userdata: locals.user,postdata: locals.posts})
    });

现在,您可以使用ExpressJS中的app.param轻松建立中间件,根据请求URL中的参数名称加载所需的数据。

http://expressjs.com/4x/api.html#app.param

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM