Node.js下/addEmployee路由使用.then前置校验员工存在性的实现咨询
现有实现的问题判定
你的当前实现是完全不正确的,根本达不到你想要的“等校验通过再执行后续操作”的效果,核心问题包括:
- 流程完全不同步:你写的
getEmployeeNum是基于回调的异步函数,既没有返回Promise,也没有任何流程阻断逻辑,主路由调用它之后,根本不会等待校验完成,会立刻往下执行密码加密、数据库插入的代码,哪怕员工已经存在,插入流程也会照常跑完。 - 服务端API误用:
getEmployeeNum里用了浏览器端才有的alert方法,Node.js环境运行这段代码会直接抛出alert is not defined的错误,根本没法给用户返回提示。 - 校验逻辑效率极低:每次校验都要把员工全表数据拉到服务端内存里循环比对,数据量稍大就会出现明显的性能瓶颈,而且代码里就算查到重复,渲染完添加员工页面之后,主流程里的数据库插入、页面跳转逻辑还是会继续执行,根本拦不住。
- 流程顺序混乱:目前代码里bcrypt的密码哈希计算还没完成,下面打开数据库连接、插入员工信息的同步代码就已经开始运行,后续用到的
pass变量根本拿不到正确的哈希值,业务逻辑完全跑不通。 - 安全隐患:所有SQL都是直接拼接用户输入的字符串,存在严重的SQL注入风险。
更优的实现方案
核心思路是先把所有带回调的异步操作(数据库查询、密码加密)封装成标准Promise,再通过Promise链式调用严格按照业务顺序执行流程,任何一步校验不通过、操作出错,直接中断后续逻辑。
具体改造步骤
- 封装统一的数据库查询方法,把重复的打开连接、执行查询、关闭连接的逻辑抽离,避免重复代码,同时从根源上避免连接泄漏。
- 把员工存在校验的SQL改成条件查询,不再全表拉数据,查到重复直接抛错中断流程。
- 按照业务顺序串起所有Promise节点:会话校验→员工号重复校验→密码哈希→员工信息入库→登录信息写入→发送注册邮件→重定向到员工列表。
- 用统一的
catch分支捕获所有错误,把提示信息返回给前端页面渲染,替代无效的alert。
优化后的参考代码
// 通用数据库查询Promise封装 const queryDb = (sql, params = []) => { return new Promise((resolve, reject) => { ibmdb.open(ibmdbconnMaster, (err, conn) => { if (err) return reject(err); conn.query(sql, params, (err, rows) => { // 查询完成主动关闭连接 conn.close(() => {}); if (err) return reject(err); resolve(rows); }); }); }); }; // 员工存在校验函数,返回Promise const checkEmployeeExist = (employeenum) => { // 直接按员工号条件查询,不全表扫描 const checkSql = "SELECT EMPLOYEE_NUM FROM EMPLOYEES WHERE EMPLOYEE_NUM = ?"; return queryDb(checkSql, [employeenum]).then(rows => { if (rows.length > 0) { throw new Error("员工编号已存在,请核对后重试"); } }); }; // bcrypt密码哈希的Promise封装 const getPasswordHash = (plainPwd) => { return new Promise((resolve, reject) => { bcrypt.hash(plainPwd, saltRounds, (err, hash) => { if (err) return reject(err); resolve(hash); }); }); }; // 改造后的添加员工路由 app.post('/addEmployee', (req, res) => { if (!req.session.loggedin) { return res.render('login.ejs'); } // 统一取请求参数,避免后续代码混写 const { firstname, lastname, username, sales, salary, location, role, admin, employee_num: employeenum, phone_num: phonenum, org, password: newPassword } = req.body; // 按业务顺序串起Promise流程 checkEmployeeExist(employeenum) .then(() => getPasswordHash(newPassword)) .then(pass => { // 使用参数化查询,彻底规避SQL注入 const addEmployeeSql = ` INSERT INTO EMPLOYEES (FIRSTNAME, LASTNAME, USERNAME, SALES, SALARY, LOCATION, ROLE, ADMIN, EMPLOYEE_NUM, PHONENUM, ORGANIZATION) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `; return queryDb(addEmployeeSql, [ firstname, lastname, username, sales, salary, location, role, admin, employeenum, phonenum, org ]).then(() => pass); // 把密码哈希值透传给下一个流程 }) .then(pass => { // 员工基础信息入库完成后,再写入登录账号信息 return addLogin(req, res, pass, firstname, lastname, username, sales, salary, location, role, admin, employeenum, phonenum); }) .then(() => { return registerEmail(username, firstname, lastname, req, res); }) .then(() => { res.redirect('/employees'); }) .catch(err => { // 统一处理所有流程错误 console.error('添加员工失败:', err); // 把错误信息传入页面渲染,给用户明确提示 res.render('addEmployee.ejs', { errorMsg: err.message }); }); });
额外注意事项
- 所有涉及用户输入的SQL都要使用占位符传参,不要直接拼接字符串,避免SQL注入风险。
- 如果后续业务逻辑更复杂,可以把Promise链换成async/await写法,可读性会比纯.then链式调用更高。
- 数据库查询完成后一定要主动关闭连接,不然高并发场景下会快速耗尽数据库连接池。
内容的提问来源于stack exchange,提问作者Gianluca
相关产品推荐
相关产品推荐

