【问题标题】:How to use events keep mongodb logic out of node.js request handlers如何使用事件将 mongodb 逻辑排除在 node.js 请求处理程序之外
【发布时间】:2012-08-11 22:51:39
【问题描述】:

我正在寻找一个包(或模式)来处理来自 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 已经做的很多重复。是否存在处理此用例的现有框架?

【问题讨论】:

    标签: node.js mongodb express


    【解决方案1】:

    这是我想出的解决方案。

    我使用了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;
    

    【讨论】:

      【解决方案2】:

      首先,我发现 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); }
      

      【讨论】:

      • 这根本不是我想要的。它不可测试,但如果它只是成为可靠的 CRUD,那可能没问题。也许我不清楚,但是 mongodb 回调的嵌套不是问题(只是示例的最简单表示)。我正在寻找的是一种不将请求/响应和数据库代码混合在一起的方法,因此可以独立测试和重构它们。
      • 当然它是可测试的......你的测试只需要实现用户和请求或资源的模拟。在这种情况下,用户是您要求的抽象层;如果它不是猫鼬模型,那么它就是您自己编写的任何 DB 包装器。
      • 在创建用户后创建模拟用户后,我可以创建一个模拟网络服务器和一个模拟 mongodb 驱动程序......
      猜你喜欢
      • 2013-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-03
      • 2021-10-15
      • 2014-07-22
      • 2016-09-11
      相关资源
      最近更新 更多