SailsJS使用一个Controller操作调用两个Service方法

我对于学习全堆栈开发,特别是后端的东西是相当陌生的,并且正试图弄清这个asynchronous的Nodejs的东西。

在提交忘记的密码表单时,我需要完成一些后端操作。 基本上控制器的行为看起来像这样( 我知道肯定是做错了 ):

forgot: function (req, res) { var token = "febfoebfoui38383303cnc"; var userEmail = req.body.email; var host = req.headers.host; AuthService.saveResetPasswordValues(token, userEmail, function (err, savedRecord) { if (err) { return res.send(404, '\n\nerror occurred\n\n'); } return res.json(savedRecord); }); AuthService.sendForgotPasswordEmail(token, userEmail, host, function (err, message) { if (err) { return res.send(404, '\n\nerror occurred\n\n'); } return res.json(message); }); }, 

每个服务都会帮助密码重置电子邮件发送出去。

 saveResetPasswordValues: function (token, userEmail, cb) { var expiration = Date.now() + 3600000; // 1 hour User.update({ email: userEmail }, { resetPasswordToken: token, resetPasswordExpires: expiration}) .exec(function (err, user) { if (err) { return cb({ status: 404, message: 'Could not find user with that email address' }); } return cb(err, user); }); }, sendForgotPasswordEmail: function (token, userEmail, host, cb) { var htmlMessage = '<h4>You are receiving this because you (or someone else) have requested the reset of the password for your account.</h4>' + '<p>Please click on the following link, or paste this into your browser to complete the process:</p>' + "<p><a href='http://" + host + '/reset/' + token + "'> Link to password reset </a></p>" + '<p>If you did not request this, please ignore this email and your password will remain unchanged.</p>'; var emailInfo = { to: userEmail, from: "customerservice@smallchangeproj.com", subject: "Small Change Project Password Reset", message: htmlMessage, fromName: "Small Change Project" }; .... some code for sending the email ... return cb(null, 'email has been sent'); } 

最后用户应该得到这样一个电子邮件:

您正在收到此邮件是因为您(或其他人)已要求重置您帐户的密码。

请点击以下链接,或将其粘贴到您的浏览器中以完成此过程:

链接到密码重置

令人惊讶的是,这个代码确实有效,但是它吐出了一堆丑陋的错误 – 告诉我,我不知道asynchronouscallback是如何工作的;)

 error: Sending 500 ("Server Error") response: Error: Can't set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader (http.js:690:11) at ServerResponse.res.setHeader (/usr/lib/node_modules/sails/node_modules/express/node_modules/connect/lib/patch.js:133:22) at ServerResponse.res.set.res.header (/home/zacharyhustles/smallChangeAPI/node_modules/sails/node_modules/express/lib/response.js:577:10) at ServerResponse.res.send (/home/zacharyhustles/smallChangeAPI/node_modules/sails/node_modules/express/lib/response.js:142:12) at ServerResponse.res.json (/home/zacharyhustles/smallChangeAPI/node_modules/sails/node_modules/express/lib/response.js:223:15) at /home/zacharyhustles/smallChangeAPI/api/controllers/UserController.js:123:15 at /home/zacharyhustles/smallChangeAPI/api/services/AuthService.js:50:4 at Object.module.exports.simpleSendEmail (/home/zacharyhustles/smallChangeAPI/api/services/EmailService.js:48:12) at Object.bound [as simpleSendEmail] (/home/zacharyhustles/smallChangeAPI/node_modules/lodash/dist/lodash.js:729:21) at Object.module.exports.sendForgotPasswordEmail (/home/zacharyhustles/smallChangeAPI/api/services/AuthService.js:46:16) at Object.bound [as sendForgotPasswordEmail] (/home/zacharyhustles/smallChangeAPI/node_modules/lodash/dist/lodash.js:729:21) at Object.module.exports.forgot (/home/zacharyhustles/smallChangeAPI/api/controllers/UserController.js:121:15) at bound (/home/zacharyhustles/smallChangeAPI/node_modules/lodash/dist/lodash.js:729:21) at routeTargetFnWrapper (/home/zacharyhustles/smallChangeAPI/node_modules/sails/lib/router/bind.js:179:5) at callbacks (/home/zacharyhustles/smallChangeAPI/node_modules/sails/node_modules/express/lib/router/index.js:164:37) at param (/home/zacharyhustles/smallChangeAPI/node_modules/sails/node_modules/express/lib/router/index.js:138:11) [Error: Can't set headers after they are sent.] 

什么是正确的方法来做到这一点? 我还想把逻辑分解成两种服务方法…

就像错误信息已经表明的那样,在你已经发送了一个响应之后,你不允许向浏览器发送另一个响应,因为在网页请求的经典请求 – 响应周期中,浏览器每个请求只接受一个响应。

您必须将第二个服务函数的调用放入第一个服务函数的callback中,并将服务帮助器的结果发送到组合对象中,例如:

  forgot: function (req, res) { var token = "febfoebfoui38383303cnc"; var userEmail = req.body.email; var host = req.headers.host; AuthService.saveResetPasswordValues(token, userEmail, function (err, savedRecord) { if (err) { return res.send(404, '\n\nerror occurred\n\n'); } AuthService.sendForgotPasswordEmail(token, userEmail, host, function (err, message) { if (err) { return res.send(404, '\n\nerror occurred\n\n'); } // make sure to adapt your client logic for this response return res.json({savedRecord: savedRecord, message: message); }); }); }, 

asynchronouscallback的一般结论是,如果您有一个函数调用依赖于前一个调用的结果,则必须将第二个调用总是嵌套到前一个调用的callback中,以便按正确的顺序调用它们第二个电话可以访问第一个电话的结果。

如果您想在一个响应中发送多个asynchronous函数的结果,只需嵌套函数调用,以便在最后最深的callback中访问所​​有结果。