Router异步中间件抛出异常失效问题求助
问题原因分析
- 异步代码执行顺序错误:原代码里的
next()写在Promise链外面,会立刻执行,不管数据库查询的异步操作有没有完成、有没有报错。这就导致哪怕权限校验失败,controller.create已经被调用了,后面的next(err)只是走错误处理流程,但正常路由流程已经跑完了。 - 同步场景下,
throw error会直接中断函数,后面的next()根本不会执行,所以能正确终止路由,把错误交给Express的错误处理中间件。 - 加
async关键字后,async函数内部的错误会被包装成Promise的reject,但如果没正确捕获或者还是提前调用了next,Express没法自动处理这个reject,就会导致错误处理失效。
解决方案
方案1:修复Promise链式调用写法
把next()移到Promise链里,只有权限校验通过才调用,错误时在catch里传错误给next:
exports.isAccess = (request, result, next) => { const userid = '64c1248482bcfbb533b366d5'; User.findOne({ _id: userid }).select({ role: 1 }) .then(user => { // 原代码里role未定义,这里要改成user.role,同时修正判断逻辑 if (user.role !== 'routerRoleResource') { const error = new Error('Authorization failed'); error.statusCode = 401; throw error; } // 校验通过才进入下一个中间件 next(); }) .catch(err => { if (!err.statusCode) err.statusCode = 500; next(err); }); // 删掉这里的next(),避免提前触发后续路由 };
方案2:用async/await写法
用try/catch捕获异步错误,同样只在校验通过时调用next():
exports.isAccess = async (request, result, next) => { try { const userid = '64c1248482bcfbb533b366d5'; const user = await User.findOne({ _id: userid }).select({ role: 1 }); if (user.role !== 'routerRoleResource') { const error = new Error('Authorization failed'); error.statusCode = 401; throw error; } // 权限没问题,继续走路由 next(); } catch (err) { if (!err.statusCode) err.statusCode = 500; next(err); } };
额外提醒
- 原代码里的
if(!role==='routerRoleResource')有两个bug:一是role没定义(应该取user.role),二是逻辑判断优先级错了,正确写法是user.role !== 'routerRoleResource'。 - Express里,async中间件的错误最好用try/catch捕获后通过
next(err)传递,部分新版本Express能自动捕获async函数的reject,但显式处理更稳妥。
内容的提问来源于stack exchange,提问作者D. Rattansingh
相关产品推荐
相关产品推荐

