如何使用事件将mongodb逻辑保留在node.js请求处理程序之外

时间:2012-08-15 15:57:59

标签: node.js mongodb express

我正在寻找一个包(或模式)来处理来自mongodb的事件,这样我就可以避免嵌套回调并将mongodb逻辑保留在我的请求处理程序之外。

现在我的代码看起来像这样:

start-express.js(服务器)

var express = require('express');
var Resource = require('express-resource');
var app = express.createServer();

// create express-resource handler  which essentially does app.get('things', ...)
var things = app.resource('things', require('./things.js'));

app.listen(port);

things.js(express-resource请求处理程序)

require('./things-provider');

// handle request  'http://example.com/things'
exports.index = function(request, response) {
    sendThings(db, response);
};

things-provider.js(处理mongodb查询)

var mongodb = require('mongodb')

// create database connection
var server = new mongodb.Server(host, port, {auto_reconnect: true});
var db = new mongodb.Db(dbName, server);

db.open(function (err, db) {
    if (err) { }
    // auto_reconnect will reopen connection when needed
});

function sendThings(db, response) {            
    db.collection('things', function(err, collection) {
        collection.find(function(err, cursor) {
            cursor.toArray(function(err, things) {
                response.send(things);
            });
        });
    });
}

module.exports.sendThings = sendThings;

我想避免将我的http响应对象传递给我的数据库处理程序,或者(更糟糕的是)在我的http响应处理程序中处理我的数据库请求。

我最近意识到我想要做的是创建一个事件处理程序来注册一个http请求/响应,并在处理和发送http响应之前等待来自数据库的响应(事件)。

这听起来像node.js已经做了很多重复。是否存在处理此用例的现有框架?

2 个答案:

答案 0 :(得分:2)

这是我提出的解决方案。

我使用mongojs大大简化了mongodb接口 - 配置灵活性的成本 - 但它隐藏了mongodb驱动程序所需的嵌套回调。它还使语法更像mongo客户端。

然后我将HTTP Response对象包装在一个闭包中,并将此闭包传递给回调中的mongodb查询方法。

var MongoProvider = require('./MongoProvider');
MongoProvider.setCollection('things');

exports.index = function(request, response){
    function sendResponse(err, data) {
        if (err) { 
            response.send(500, err);
        }    
        response.send(data);
    };

    MongoProvider.fetchAll(things, sendResponse);
};

它基本上只是将响应对象传递给数据库提供程序,但是通过将其包装在知道如何处理响应的闭包中,它将该逻辑保留在我的数据库模块之外。

稍微改进是使用函数在我的请求处理程序之外创建响应处理程序闭包:

function makeSendResponse(response){
    return function sendResponse(err, data) {
        if (err) {
            console.warn(err);
            response.send(500, {error: err});
            return;
        }

        response.send(data);
    };
}

所以现在我的请求处理程序看起来像这样:

exports.index = function(request, response) {
    response.send(makeSendResponse(response));
}

我的MongoProvider看起来像这样:

var mongojs = require('mongojs');

MongoProvider = function(config) {
this.configure(config);
    this.db = mongojs.connect(this.url, this.collections);
}

MongoProvider.prototype.configure = function(config) {
    this.url = config.host + "/" + config.name;
    this.collections = config.collections;
}

MongoProvider.prototype.connect = function(url, collections) {
    return mongojs.connect(this.url, this.collections);
}

MongoProvider.prototype.fetchAll = function fetchAll(collection, callback) {
    this.db(collection).find(callback);
}

MongoProvider.prototype.fetchById = function fetchById(id, collection, callback) {
    var objectId = collection.db.bson_serializer.ObjectID.createFromHexString(id.toString());

    this.db(collection).findOne({ "_id": objectId }, callback);
}

MongoProvider.prototype.fetchMatches = function fetchMatches(json, collection, callback) {
    this.db(collection).find(Json.parse(json), callback);
}

module.exports = MongoProvider;

我还可以为特定集合扩展MongoProvider以简化API并进行其他验证:

ThingsProvider = function(config) {
    this.collection = 'things';
    this.mongoProvider = new MongoProvider(config);
    things = mongoProvider.db.collection('things');
}

ThingsProvider.prototype.fetchAll = function(callback) {
    things.fetchAll(callback);
}

//etc...

module.exports = ThingsProvider;

答案 1 :(得分:0)

好吧,首先我发现Mongoose在结构良好的应用程序中比直接mongo更容易使用。这可能会对你有帮助。

其次,我认为您尝试做的事情可以通过中间件(应用程序级别或路由级别)轻松完成,因为您已经使用了express。或者,参数过滤,如果您的查询将根据参数变化。我在上一次看到的模式看起来像这样:

var User = mongoose.model("user'); // assumes your schema is previously defined

app.param('user_id', function(req,res,next, id){
   User.find(id, function(err,user){
      if(err) next(err);
      else { 
         req.user = user; 
         next();
      }
   });
}); 

它仍然有一些嵌套,但不像你的例子那么糟糕,更易于管理。然后,假设您有一个'/ profile'端点,您可以这样做:

app.get('/profile/:user_id', function(req,res){ res.render('profile', req.user); }