node.js - SailsJS 使用一个 Controller 操作调用两个 Service 方法

标签 node.js express sails.js

我对学习全栈开发相当陌生,特别是后端方面,并且正在尝试弄清楚这个异步 Nodejs 的东西。

提交忘记密码表单后,我需要完成一些后端操作。基本上 Controller 操作看起来像这样(我知道这肯定是以错误的方式完成的):

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');

}

最后用户应该会收到如下所示的电子邮件:

You are receiving this because you (or someone else) have requested the reset of the password for your account.

Please click on the following link, or paste this into your browser to complete the process:

Link to password reset

令人惊讶的是,这段代码确实有效,但它抛出了一堆丑陋的错误 - 告诉我我不知道异步回调是如何工作的;)

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.]

执行此操作的正确方法是什么?我仍然想将逻辑分成两个服务方法......

最佳答案

就像错误消息已经暗示的那样,在已经发送了一个响应之后,您不能再向浏览器发送另一个响应,因为在网页请求的经典请求-响应周期中,浏览器每个请求仅接受一个响应。

您必须将对第二个服务函数的调用放入第一个服务函数的回调中,并在组合对象中发送服务助手的结果,例如像这样:

    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);
            });
        });
    },

异步回调的一般结论是,如果您有一个函数调用依赖于前一个调用的结果,则必须将第二个调用始终嵌套到前一个调用的回调中,以便以正确的顺序调用它们,并且第二个调用可以访问第一个调用的结果。

如果您想在一个响应中发送多个异步函数的结果,只需嵌套函数调用,以便您可以访问最终最深回调中的所有结果。

关于node.js - SailsJS 使用一个 Controller 操作调用两个 Service 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31632556/

相关文章:

javascript - 如何使用 sequelize 或 sql 聚合来自不同表的总和?

node.js - express 中间件测试 mocha chai

node.js - 如何将多个POST参数传递给mongodb并获得匹配结果

express - 是否可以在 Sails.js 中使用多个模板引擎?

javascript - sailsjs 其中多对多关系包含

mysql - 如何为现有的 sails 项目设置 sequelize

node.js - 在 Visual Studio Code 中调试输出 nodejs

node.js - Node Js 和 Sequelize 模型从两个表中获取数据

node.js - Mongoose - 从现有数据库数据创建模型

javascript - 如何为每个 Web 套接字仅使用一个数据库连接?