Node.js/Express注册流程中undefined导致promise链异常中断问题排查
Node.js/Express 注册流程问题解决方案
Promise链中断返回undefined的根因
Promise链式调用要求每个.then()回调必须返回有效值(普通值/Promise实例),才能把数据传递到下一个链式节点。如果邮箱校验的.then()回调仅打印日志,没有返回后续哈希操作需要的用户数据,也没有返回哈希操作的Promise实例,下一个.then()接收的入参就是undefined,会直接触发后续逻辑异常,跳到.catch()分支输出注册失败日志。
错误Promise链示例
validateInput(userData) .then(data => checkEmail(data.email)) .then(isExist => { if(isExist) throw new Error('邮箱已注册') console.log('邮箱不存在') // 此处无return语句,下一个.then入参为undefined }) .then(undefinedData => hashPassword(undefinedData.password)) // 直接抛出异常 .catch(err => console.log('注册失败', err))
正确Promise链写法
validateInput(userData) .then(data => { // 同时返回校验后的用户数据和邮箱查重结果 return checkEmail(data.email).then(isExist => ({ isExist, validData: data })) }) .then(({ isExist, validData }) => { if(isExist) throw new Error('邮箱已注册') console.log('邮箱不存在') // 并行执行哈希操作,返回处理后的用户数据 return Promise.all([ bcrypt.hash(validData.password, 10), bcrypt.hash(validData.username, 10) ]).then(([pwdHash, usernameHash]) => ({ ...validData, password: pwdHash, username: usernameHash })) }) .then(finalData => pool.query( 'INSERT INTO users (username, email, password) VALUES ($1, $2, $3)', [finalData.username, finalData.email, finalData.password] )) .then(() => console.log('注册成功')) .catch(err => console.log('注册失败', err))
pg库邮箱查重函数问题
你的猜测正确,邮箱查重逻辑错误的根因是checkEmail函数对pg查询结果的处理不符合规范。pg库的query方法返回的结果是一个固定结构对象,匹配到的行数据存放在rows属性中,直接判断返回对象的长度会永远返回错误结果。
错误checkEmail写法示例
// 错误1:直接返回查询结果对象,未提取rows属性 const checkEmail = (email) => { return pool.query('SELECT 1 FROM users WHERE email = $1 LIMIT 1', [email]) } // 错误2:判断逻辑错误,查询结果对象本身有多个内置属性,Object.keys长度永远大于0 const checkEmail = async (email) => { const res = await pool.query('SELECT 1 FROM users WHERE email = $1 LIMIT 1', [email]) return Object.keys(res).length > 0 }
正确checkEmail写法
const checkEmail = async (email) => { // 参数化查询避免SQL注入 const { rows } = await pool.query( 'SELECT id FROM users WHERE email = $1 LIMIT 1', [email] ) // rows为匹配到的行数组,长度大于0说明邮箱已存在 return rows.length > 0 }
修复逻辑说明
你通过Object.keys判断返回结果长度修复问题,本质是你调整了返回逻辑,把匹配到的行数据转成了对象再判断长度。更推荐直接用rows数组的length属性判断,性能更高,逻辑更直观。
推荐async/await写法(可读性更强)
const register = async (userData) => { try { // 1. 非法字符校验 const validData = await validateInput(userData) // 2. 邮箱查重 const emailExist = await checkEmail(validData.email) if(emailExist) throw new Error('邮箱已注册') // 3. 哈希处理 const pwdHash = await bcrypt.hash(validData.password, 10) const usernameHash = await bcrypt.hash(validData.username, 10) // 4. 数据入库 await pool.query( 'INSERT INTO users (username, email, password) VALUES ($1, $2, $3)', [usernameHash, validData.email, pwdHash] ) console.log('注册成功') } catch (err) { console.log('注册失败', err) throw err } }
内容的提问来源于stack exchange,提问作者Pangit
相关产品推荐
相关产品推荐

