moj*_*ker 14 javascript mongoose mongodb node.js express
我正在尝试创建一个多租户应用程序(saas),其中每个客户端都有自己的数据库.
我的情况是:
我创建了一个中间件来确定客户端是基于子域的,然后从通用数据库中检索客户端的数据库连接信息.我不知道如何为此客户端建立连接对象,以便能够在我的控制器中使用.我应该在中间件或控制器中执行此操作吗?如果它在模型中,我如何传递连接字符串和参数(我可以使用会话,但我不知道如何从模型内访问会话).
我该怎么做?
这是我的中间件的一个例子,我想创建一个mongoose连接,我想动态(传递客户端的连接信息):
function clientlistener() {
return function (req, res, next) {
console.dir('look at my sub domain ' + req.subdomains[0]);
// console.log(req.session.Client.name);
if (req.session.Client && req.session.Client.name === req.subdomains[0]) {
var options = session.Client.options;
var url = session.Client.url
var conn = mongoose.createConnection(url, options);
next();
}
}
}
Run Code Online (Sandbox Code Playgroud)
如何从控制器内部访问此连接对象?还是从模特?
谢谢.
moj*_*ker 14
这是为了帮助那些可能发现自己处于类似情况的人.我希望它可以标准化.我不认为每当有人需要制作多租户应用程序时我们就必须重新发明轮子.
此示例描述了一个多租户结构,每个客户端都有自己的数据库.就像我说的那样,可能有更好的方法,但由于我自己没有得到帮助,这是我的解决方案.
所以这个解决方案的目标是:
在你的app.js文件中
app.use(clientListener()); // checks and identify valid clients
app.use(setclientdb());// sets db for valid clients
Run Code Online (Sandbox Code Playgroud)
我创建了两个中间件:
clientListener - 识别客户端连接,setclientdb - 在识别客户端后,从Master数据库获取客户端详细信息,然后建立与客户端数据库的连接.我通过检查请求对象中的子域来检查客户端是谁.我做了一堆检查以确保客户端有效(我知道代码很乱,并且可以变得更干净).确保客户端有效后,我将客户端信息存储在会话中.我还检查如果客户端信息已经存储在会话中,则无需再次查询数据库.我们只需要确保请求子域匹配已存储在会话中的子域.
var Clients = require('../models/clients');
var basedomain = dbConfig.baseDomain;
var allowedSubs = {'admin':true, 'www':true };
allowedSubs[basedomain] = true;
function clientlistener() {
return function(req, res, next) {
//console.dir('look at my sub domain ' + req.subdomains[0]);
// console.log(req.session.Client.name);
if( req.subdomains[0] in allowedSubs || typeof req.subdomains[0] === 'undefined' || req.session.Client && req.session.Client.name === req.subdomains[0] ){
//console.dir('look at the sub domain ' + req.subdomains[0]);
//console.dir('testing Session ' + req.session.Client);
console.log('did not search database for '+ req.subdomains[0]);
//console.log(JSON.stringify(req.session.Client, null, 4));
next();
}
else{
Clients.findOne({subdomain: req.subdomains[0]}, function (err, client) {
if(!err){
if(!client){
//res.send(client);
res.send(403, 'Sorry! you cant see that.');
}
else{
console.log('searched database for '+ req.subdomains[0]);
//console.log(JSON.stringify(client, null, 4));
//console.log(client);
// req.session.tester = "moyo cow";
req.session.Client = client;
return next();
}
}
else{
console.log(err);
return next(err)
}
});
}
}
}
module.exports = clientlistener;
Run Code Online (Sandbox Code Playgroud)
我再次检查所有内容,确保客户端有效.然后打开与从会话中检索的信息到客户端数据库的连接.
我还确保将所有活动连接存储到全局对象中,以便在每次请求时阻止与数据库的新连接(我们不希望使用连接重载每个客户端mongodb服务器).
var mongoose = require('mongoose');
//var dynamicConnection = require('../models/dynamicMongoose');
function setclientdb() {
return function(req, res, next){
//check if client has an existing db connection /*** Check if client db is connected and pooled *****/
if(/*typeof global.App.clientdbconn === 'undefined' && */ typeof(req.session.Client) !== 'undefined' && global.App.clients[req.session.Client.name] !== req.subdomains[0])
{
//check if client session, matches current client if it matches, establish new connection for client
if(req.session.Client && req.session.Client.name === req.subdomains[0] )
{
console.log('setting db for client ' + req.subdomains[0]+ ' and '+ req.session.Client.dbUrl);
client = mongoose.createConnection(req.session.Client.dbUrl /*, dbconfigoptions*/);
client.on('connected', function () {
console.log('Mongoose default connection open to ' + req.session.Client.name);
});
// When the connection is disconnected
client.on('disconnected', function () {
console.log('Mongoose '+ req.session.Client.name +' connection disconnected');
});
// If the Node process ends, close the Mongoose connection
process.on('SIGINT', function() {
client.close(function () {
console.log(req.session.Client.name +' connection disconnected through app termination');
process.exit(0);
});
});
//If pool has not been created, create it and Add new connection to the pool and set it as active connection
if(typeof(global.App.clients) === 'undefined' || typeof(global.App.clients[req.session.Client.name]) === 'undefined' && typeof(global.App.clientdbconn[req.session.Client.name]) === 'undefined')
{
clientname = req.session.Client.name;
global.App.clients[clientname] = req.session.Client.name;// Store name of client in the global clients array
activedb = global.App.clientdbconn[clientname] = client; //Store connection in the global connection array
console.log('I am now in the list of active clients ' + global.App.clients[clientname]);
}
global.App.activdb = activedb;
console.log('client connection established, and saved ' + req.session.Client.name);
next();
}
//if current client, does not match session client, then do not establish connection
else
{
delete req.session.Client;
client = false;
next();
}
}
else
{
if(typeof(req.session.Client) === 'undefined')
{
next();
}
//if client already has a connection make it active
else{
global.App.activdb = global.App.clientdbconn[req.session.Client.name];
console.log('did not make new connection for ' + req.session.Client.name);
return next();
}
}
}
}
module.exports = setclientdb;
Run Code Online (Sandbox Code Playgroud)
由于我使用的是mongoose和native mongo的组合,我们必须在运行时编译我们的模型.请看下面
将此添加到您的 app.js
// require your models directory
var models = require('./models');
// Create models using mongoose connection for use in controllers
app.use(function db(req, res, next) {
req.db = {
User: global.App.activdb.model('User', models.agency_user, 'users')
//Post: global.App.activdb.model('Post', models.Post, 'posts')
};
return next();
});
Run Code Online (Sandbox Code Playgroud)
说明:
就像我之前说过的,我创建了一个存储活动数据库连接对象的全局对象: global.App.activdb
然后我将此连接对象用于创建(编译)mongoose模型,之后将其存储在req对象的db属性中:req.db.我这样做,以便我可以在我的控制器中访问我的模型,例如这样.
我的用户控制器示例:
exports.list = function (req, res) {
req.db.User.find(function (err, users) {
res.send("respond with a resource" + users + 'and connections ' + JSON.stringify(global.App.clients, null, 4));
console.log('Worker ' + cluster.worker.id + ' running!');
});
};
Run Code Online (Sandbox Code Playgroud)
我会最终回来清理它.如果有人想帮助我,那就太好了.
因此,此解决方案的目标是:
**
** 特征
测试请求是否不是租户请求;即(请求应用首页,管理页面等)
'use strict';
/**
* Created by moyofalaye on 3/17/14.
*/
var path = require('path');
var config = require('../../config/config');
// Globbing model files
config.getGlobbedFiles('./app/models/*.js').forEach(function (modelPath) {
require(path.resolve(modelPath));
});
function modelsInit() {
return function (req, res, next) {
//console.log(req.subdomains[0]);
switch (req.subdomains[0]) {
case 'www':
case undefined:
return next();
break;
case 'admin':
return next();
break;
// default:
// return
}
var clientname = req.session.Client.name;
// test if models are not already compiled if so, skip
if (/*typeof req.db === 'undefined' && */ typeof global.App.clientModel[clientname] === 'undefined') {
req.db = {};
//Get files from models directory
config.getGlobbedFiles('./app/models/clientmodels/**/*.js').forEach(function (modelPath) {
console.log('the filepath is ' + modelPath);
//Deduce/ extrapulate model names from the file names
//Im not very good with regxp but this is what i had to do, to get the names from the filename e.g users.server.models.js (this is my naming convention, so as not to get confused with server side models and client side models
var filename = modelPath.replace(/^.*[\\\/]/, '');
var fullname = filename.substr(0, filename.lastIndexOf('.'));
var endname = fullname.indexOf('.');
var name = fullname.substr(0, endname);
req.db[name] = require(path.resolve(modelPath))(global.App.activdb);
console.log('the filename is ' + name);
});
global.App.clientModel[clientname] = req.db;
console.log(global.App.clients);
return next();
}
// since models exist, pass it to request.db for easy consumption in controllers
req.db = global.App.clientModel[clientname];
return next();
};
}
module.exports = modelsInit;
Run Code Online (Sandbox Code Playgroud)待办事项:进一步说明
var config = require('../../config/config');
var Clients = require('../models/clients');
var basedomain = config.baseDomain;
var allowedSubs = {'admin': true, 'www': true};
allowedSubs[basedomain] = true;
//console.dir(allowedSubs);
function clientlistener() {
return function (req, res, next) {
//check if client has already been recognized
if (req.subdomains[0] in allowedSubs || typeof req.subdomains[0] == 'undefined' || req.session.Client && req.session.Client.name === req.subdomains[0]) {
console.log('did not search database for ' + req.subdomains[0]);
//console.log(JSON.stringify(req.session.Client, null, 4));
return next();
}
//look for client in database
else {
Clients.findOne({subdomain: req.subdomains[0]}, function (err, client) {
if (!err) {
//if client not found
if (!client) {
//res.send(client);
res.status(403).send('Sorry! you cant see that.');
console.log(client);
}
// client found, create session and add client
else {
console.log('searched database for ' + req.subdomains[0]);
req.session.Client = client;
return next();
}
}
else {
console.log(err);
return next(err)
}
});
}
}
}
module.exports = clientlistener;
Run Code Online (Sandbox Code Playgroud)
var client;
var clientname;
var activedb;
var Promise = require("bluebird");
Promise.promisifyAll(require("mongoose"));
//mongoose = require('mongoose');
function setclientdb() {
return function (req, res, next) {
//check if client is not valid
if (typeof(req.session.Client) === 'undefined' || req.session.Client && req.session.Client.name !== req.subdomains[0]) {
delete req.session.Client;
client = false;
return next();
}
//if client already has an existing connection make it active
else if (global.App.clients.indexOf(req.session.Client.name) > -1) {
global.App.activdb = global.App.clientdbconn[req.session.Client.name]; //global.App.clientdbconnection is an array of or established connections
console.log('did not make new connection for ' + req.session.Client.name);
return next();
}
//make new db connection
else {
console.log('setting db for client ' + req.subdomains[0] + ' and ' + req.session.Client.dbUrl);
client = mongoose.createConnection(req.session.Client.dbUrl /*, dbconfigoptions*/);
client.on('connected', function () {
console.log('Mongoose default connection open to ' + req.session.Client.name);
//If pool has not been created, create it and Add new connection to the pool and set it as active connection
if (typeof(global.App.clients) === 'undefined' || typeof(global.App.clients[req.session.Client.name]) === 'undefined' && typeof(global.App.clientdbconn[req.session.Client.name]) === 'undefined') {
clientname = req.session.Client.name;
global.App.clients.push(req.session.Client.name);// Store name of client in the global clients array
activedb = global.App.clientdbconn[clientname] = client; //Store connection in the global connection array and set it as the current active database
console.log('I am now in the list of active clients ' + global.App.clients[clientname]);
global.App.activdb = activedb;
console.log('client connection established, and saved ' + req.session.Client.name);
return next();
}
});
// When the connection is disconnected
client.on('disconnected', function () {
console.log('Mongoose ' + req.session.Client.name + ' connection disconnected');
});
// If the Node process ends, close the Mongoose connection
process.on('SIGINT', function () {
client.close(function () {
console.log(req.session.Client.name + ' connection disconnected through app termination');
process.exit(0);
});
});
}
}
}
module.exports = setclientdb;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6387 次 |
| 最近记录: |