Sails 1模型生命周期中使用密码加密Helper出现请求循环无响应问题
嘿,这种请求卡成循环没响应的情况太闹心了!我之前在做Sails项目的时候也碰到过类似的问题,大概率是你的密码加密Helper和用户模型的生命周期钩子配合出了点小问题,导致请求一直在无限触发钩子流程,没法正常走完。下面给你分析几个最常见的原因和解决办法:
这是最常见的坑!比如你在beforeCreate或者beforeUpdate钩子里面,手动调用了user.save()或者类似的方法——但Sails的生命周期钩子本身就是在模型保存前自动触发的,你这么做相当于触发了新的保存请求,进而再次调用钩子,无限循环就这么来了。
正确的做法是:直接修改传入的属性值,然后调用proceed()就行,完全不用手动调用save。举个例子:
// api/models/User.js module.exports = { attributes: { // 你的模型属性定义... }, beforeCreate: async function(values, proceed) { // 调用你的加密Helper处理密码 values.password = await PasswordHelper.encrypt(values.password); // 直接放行,让Sails继续执行后续的创建流程 return proceed(); } };
如果你的加密方法是异步的(比如用bcrypt.hash这种返回Promise的方法),但你在钩子里面没有用await等待它完成,就会导致钩子提前放行,而密码加密还没完成,甚至会让整个请求一直处于等待状态,看起来就像“无响应”。
检查并修正你的Helper:
首先确保Helper的加密方法返回Promise,或者本身就是async函数:
// api/helpers/password-encrypt.js module.exports = { friendlyName: 'Encrypt password', description: 'Encrypt plain text password', inputs: { password: { type: 'string', required: true } }, fn: async function(inputs) { // 这里用bcrypt举例,你也可以用其他加密库 const bcrypt = require('bcrypt'); const hash = await bcrypt.hash(inputs.password, 10); return hash; } };
然后在模型钩子里面必须用await调用这个Helper,不然异步逻辑会“跑飞”,导致后续流程出问题。
如果你的加密逻辑不小心修改了username、email这类有唯一性校验或者其他钩子监听的字段,就可能触发其他生命周期钩子,进而引发连锁循环。
避坑提醒:在处理密码加密时,只专注修改password字段,不要碰其他任何模型属性,避免意外触发额外的钩子逻辑。
检查你的user/create Action,是不是在里面先手动加密了密码,然后模型钩子又再次加密?虽然这不会直接导致循环,但如果Action里不小心重复调用了User.create(),或者逻辑处理不当,也可能让请求卡住。
推荐的Action2写法:
把加密逻辑完全交给模型钩子处理,Action只负责接收参数、调用创建方法和返回结果:
// api/controllers/user/create.js module.exports = { friendlyName: 'Create user', description: 'Action to create a new user', inputs: { name: { description: 'User full name', type: 'string', required: true }, username: { description: 'Unique username', type: 'string', required: true }, password: { description: 'User plain text password', type: 'string', required: true }, email: { description: 'User email address', type: 'string', required: true } }, exits: { success: { description: 'User created successfully' }, usernameTaken: { responseType: 'conflict', description: 'Username is already in use' } }, fn: async function(inputs, exits) { try { // 直接传入原始密码,加密交给模型钩子处理 const newUser = await User.create(inputs).fetch(); // 返回敏感信息之外的用户数据 return exits.success({ id: newUser.id, name: newUser.name, username: newUser.username, email: newUser.email }); } catch (err) { // 处理唯一性冲突 if (err.code === 'E_UNIQUE') { return exits.usernameTaken(); } // 其他错误直接抛出 throw err; } } };
按照上面的思路排查一遍,应该就能解决循环无响应的问题啦!
内容的提问来源于stack exchange,提问作者Leonardo Freitas

