在 Express 项目中使用 Waterline

jopen 8年前

在上一篇Node.js ORM 数据操作中间件 Waterline 中,我们介绍了 Waterline 的功能与特点,这篇文章中我们将用一个实例 waterline-sample (GithubCoding.NET),来看看在 Express 项目中如何使用 Waterline。

要在项目中使用 Waterline ,无非是解决如何配置,在什么时机初始化,怎样组织所有的数据集合,以及在控制器中怎么调用 Waterline 中的数据集合这几个问题。

项目的功能与结构

这个示例项目中,将会实现两个关于 Post 的 API ,添加和获取列表的接口,功能比较简单,但代码的组织,依然遵循模块化和 MVC 的设计原则,主要的文件和目录如下:

|-- app  |   |-- controllers  |   |   `-- post.server.controller.js  |   |-- models  |   |   `-- post.server.model.js  |   `-- routes  |       `-- post.server.routes.js  |-- config  |   |-- config.js  |   |-- env  |   |   `-- development.js  |   |-- express.js  |   `-- waterline.js  |-- app.js  |-- bin  |   `-- www  `-- package.json

其中,config目录存储基本配置、 Express 配置和 Waterline 的配置文件,其中的env目录存储根据环境而不同的基本配置。app目录下的三个文件夹,分别按 MVC 的结构组织 Waterline 的 Collections ,Expresss 的控制器以及路由文件。根目录下的app.js将使用config/express.js中的配置来生成 Express 的实例,而入口文件bin/www则完成 Waterline 的初始化和启动 Express 实例的端口监听。

数据集合的组织

所有的数据集合,全部组织在app/models目录中,直接使用module.exports来将Waterline.Collections实例导出,以便在 Waterline 的配置文件中调用。

var Waterline = require('waterline');    module.exports = Waterline.Collection.extend({    identity: 'post',    connection: 'mongo',    schema: true,    attributes: {      title: {        type: 'string',        required: true      },      content: 'string',      createTime: 'date',      lastModifyTime: 'date'    },    beforeCreate: function(v, cb){      v.createTime = new Date();      return cb();    },    print: function(v) {      console.log('\tTitle:', v.title, 'create at:', v.createTime);      console.log('\tContent:', v.content);    }  });

这里也演示了如何定义生命周期回调和自定义的方法。

配置

配置里主要是生成 Waterline 的实例,并加载上面的数据集合配置文件。

var Waterline = require('waterline');  var mongoAdapter = require('sails-mongo');  var config = require('./config');    // models  var Post = require('../app/models/post.server.model');    var orm = new Waterline();  var wlconfig = {    adapters: {      'default': mongoAdapter,      mongo: mongoAdapter    },    connections: {      'mongo': {        adapter: 'mongo',        url: config.mongo      }    }  };  orm.loadCollection(Post);    exports.orm = orm;  exports.config = wlconfig;

这里使用的是exports来导出,因为我们有两个值需要导出,一个是 Waterline 的实例,另外一个是 Waterline 的初始化配置。这在初始化的时候会用到。

初始化

初始化是在bin/www里完成的,原因是保证 Express 启动监听,必须在 Waterline 的成功初始化之后进行。

var app = require('../app');  var config = require('../config/config');  var waterline = require('../config/waterline');    waterline.orm.initialize(waterline.config, function(err, models){    if(err) {      console.log('waterline initialize failed, err:', err);      return;    }    console.log('waterline initialize success.');        app.set('models', models.collections);      app.listen(config.port, function(){      console.log('Express listening on port:', config.port);    });  });

初始化是直接使用 Waterline 实例的intialize()方法,需要传入对应的配置,这两个都是在waterline.js配置文件中导出的。为了方便我们在控制器代码中调用 Waterline 的数据集合,这里先将它加入到 Express 实例的配置列表中。

在 Express 控制器中使用 Waterline 数据集合

由于 Waterline 的初始化过程是异步的,所以我们没有办法直接使用module.exports或exports方法来导出它的实例,也就无法直接以 JavaScript 模块化的方式调用它实例中的数据集合。这里将借助 Express 的实例,来在控制器代码中使用它。

但实际上,在控制器代码中,也是没有办法直接访问 Express 的实例的,所以这里我们在 Express 的配置里,增加一个中间件,将附加在 Express 实例上的数据集合,再加入到 Express 请求对象中,这样便可以在控制器代码中通过请求对象来访问 Waterline 实例的数据集合了。当然,加到响应对象也可以。

var express = require('express');  var waterline = require('./waterline');    module.exports = function(){    console.log('express initialing...');    var app = express();    // ...    app.use(function(req, res, next){      req.models = app.get('models');      next();    });      require('../app/routes/post.server.routes')(app);    // ...    return app;  };

在控制器里,便可以通过请求对象的models成员来调用了。

module.exports = {    list: function(req, res, next){      var page = parseInt(req.query.page, 1) ? parseInt(req.query.page, 1) : 1;      var limit = parseInt(req.query.limit, 1) ? parseInt(req.query.limit, 1) : 1;      req.models.post.find().paginate({page: page, limit: limit}).exec(function(err, docs){        res.json(docs);      });    }  };

虽然也可以通过全局变量来调用数据集合,不过在有其它办法的情况下,还是尽量不要使用全局变量吧。

来自:http://chensd.com/2015-10/Use-Waterline-in-Express-project.html