1

我使用 Node.js 和 Expressjs 进行服务器端编码,使用 MongoDB 作为后端。我对所有这些技术都是新手。我需要根据请求执行操作列表。

例如在用户管理中

  1. 检查用户是否已经注册
  2. 如果已注册,请重新发送激活电子邮件
  3. 如果未注册,则从另一个表中获取 userId,该表将维护用户、资产等的 id。[我知道 MongoDB 提供唯一的 _id;但我需要有一个唯一的整数 id 作为 userId]
  4. 创建用户
  5. 发送成功或失败响应。

为了实现这一点,我编写了以下代码:

exports.register = function(req,res,state,_this,callback) {
   switch(state) {
    case 1: //check user already registered or not
      _this.checkUser(req, res, ( state + 1 ), _this, _this.register, callback);
      break;
    case 2: //Already registered user so resend the activation email
      _this.resendEmail(req, res, 200, _this, _this.register, callback);
      break;
    case 3: //not registered user so get the userId from another table that will maintain the ids for user,assets etc
      _this.getSysIds(req, res, ( state + 2 ), _this, _this.register, callback);
      break;
    case 4: //create the entry in user table
      _this.createUser(req, res, ( state + 1 ), _this, _this.register, callback);
      break;
    case 200: //Create Success Response
      callback(true);
      break;
    case 101://Error
      callback(false);
      break;
    default:
      callback(false);
    break;
  }
};

检查用户代码是这样的

exports.checkUser = function(req,res,state,_this,next,callback) {
    //check user already registered or not
    if(user) {//Already registered user so resend the activation email
       next(req,res,state,_this,callback);
    }
    else {//not registered user so get the userId
      next(req,res,(state + 1),_this,callback);
    }
}

和类似的其他功能。

对注册函数的第一次调用将从 app.get 执行为

user.register(req,res,1,this,function(status) {
//do somthing
});

有没有更好的方法来做到这一点?我面临的问题是基于某些条件,我必须遵循一系列行动。我可以在嵌套的回调结构中编写所有这些,但在这种情况下,我无法重用我的代码。

我的老板告诉我的一个问题是,在代码中我调用了函数寄存器并将其放入一个回调堆栈中

状态 1:切库斯

状态 3:getIds

状态4:创建用户

最后在状态 200 中,我刚刚从堆栈中退出?可能会导致堆栈溢出!

有没有更好的方法来处理 Node.js/Expressjs 中的回调?

注意:以上是示例代码。我有很多不同的情况。

4

2 回答 2

3

也许更优雅的方法是将所有各种可能的功能放入一个对象中,然后根据您所处的状态调用您需要的任何一个。有点像

var States = {
   1: checkuser,
   2: resendEmail,
   3: getSysIds,
   4: createUser,
   5: whateverIsNext 
}

然后你只需要一个可以完成所有事情的功能

function exec(req,res,state,_this,callback){
   States[state].apply(this, arguments) 
}

你的每个函数都会将 state 参数设置为它需要的任何值,然后调用 exec,例如

function checkUser(req,res,state,_this,callback){
    var doneRight = do_whatever_you_need;
    state = doneRight? state++: state; //just set state to whatever number it should be
    exec(req,res,state,_this,callback);
}

这样,您的步骤列表就很好了,可读/可更改。您拥有一个执行所有操作的函数,并且每个步骤只需要管理下一步应该是什么。如果您愿意,您还可以用命名替换编号的步骤,这将使其更具可读性,但您将失去增加/减少步骤变量的能力。

于 2012-12-23T07:26:44.527 回答
2

您在这里过度设计混合中间件、数据库层和路由处理程序。让我们让它更简单,更可重用。

首先,让我们确保我们的中间件和路由处理程序可以相互通信。这可以通过会话中间件来完成:

// Somewhere inside configuration:
app.use(express.cookieParser()
app.use(express.session({secret: 'cookie monster'));

// If you don't want sessions, change with this:
app.use(function (req, res, next) {
  req.session = {};
  next();
});

您现在想要的是在请求处理期间到处都有用户信息。目前,它只是电子邮件,无论它是否注册。那将是第一个独立的中间件。email为简洁起见,我们假设您通过使用from query string查询 db 来检测用户是否注册。

// Fills `req.session.user` with user info (email, registration). 
function userInfo (req, res, next) {
  var user = req.session.user = req.session.user || {};
  user.email = req.query.email;  // I'll skip validation.

  // I'm not sure which mongo driver you are using, so this is more of a pseudo-code.
  db.users.findOne({email: user.email}, function(err, bson) {
    if (err) {
      return next(err);  // Express 3 will figure that error occured
                         // and will pass control to error handler:
                         // http://expressjs.com/guide.html#error-handling
    }

    // Could remember information from DB, but we'll just set `registred` field.
    user.registred = !!bson;
    next();
  });
}

确定注册状态后,您应该发送电子邮件或创建新用户,这将是请求处理程序(最后一个参数app.get)。在执行处理程序之前,我们希望我们userInfo运行(可以只传递userInfo(不是数组),但我个人更喜欢这种方式):

app.get('/register', [userInfo], function (req, res) {
  var user = req.session.user;
  var fn = user.registred ? sendActivationEmail
                          : registerUser;

  // If signatures differs or you need different reply for cases...
  // Well, you'll figure.
  fn(user.email, function (err) {
    return res.send(err ? 500 : 200);
  });
});

请求处理程序不应该被这两个函数内部发生的事情所困扰(多少数据库查询,谁在发送电子邮件以及如何发送电子邮件)。它所知道的是,如果发生错误,提供的回调的第一个参数不为空。

现在关于创建新用户。编写registerUser函数,它将查询带有 ID 的表,准备用户信息并将其保存在数据库中。关于如何同步这两个操作,请参阅async和其他SO questions。创建完成或出错时,调用callback结果。这个函数应该是 DB 层的一部分。至少,创建db.js模块并导出重要的东西,例如registerUser;通过电子邮件(在第一个中间件中)查询用户信息也应该在 DB 层中。

类似的适用于sendActivationEmail

旁注:您可以在 mongo documet中放入任何您想要_id的内容,而不仅仅是ObjectId.

于 2012-12-23T19:34:44.453 回答