You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

三个错误处理中间件搭配try-catch仍无法阻止应用崩溃,求排查

问题描述

希望Node.js应用在出错时不崩溃,仅通过res.send返回错误信息,但当前配置未生效。已将错误中间件挂载在路由末尾,接口中也用了try-catch捕获异常,但应用仍会崩溃。

路由配置

router.post('/signUp',signupmiddlewere.signup2error,Routes.signUp)
router.post('/login',Routes.login)
router.post('/jwtverify',Routes.jwtverify)
router.use(errormiddelware)
router.use(usererrorFunction)
router.use(regularerrorFunction)

/jwtverify接口代码

module.exports.jwtverify = async (req,res,next)=>{
    try {
        const {jwt} = req.body
        
        let objectid = 1

        JWT.verify(jwt, 'secret', function(err, decoded) {
            objectid = decoded.id._id
        });

        let user = 0  

        await User.findOne({_id:objectid}).then((result)=>{
            user = result
        }).catch((err)=>{
            next(err)
        })

        res.status(200).send({
            status:'success',
            user:user
        })

    } catch (error) {
        next(error) 
    }
}

错误处理中间件

  1. regularerrorFunction
const regularerrorFunction = (error,req,res,next)=>{
    return res.json(error.message)
}
module.exports = regularerrorFunction
  1. errorFunction(返回500状态码)
const errorFunction = (error,req,res,next)=>{
    return res.status(500).json(error.message)
}
module.exports = errorFunction
  1. usererrorFunction(返回400状态码)
const usererrorFunction = (error,req,res,next)=>{
    return res.status(400).json(error.message)
}
module.exports = usererrorFunction

问题根源

  1. JWT.verify回调中的未捕获错误
    JWT.verify用的是回调模式,如果传入无效JWT,err触发后decoded会是undefined,此时直接访问decoded.id._id会抛出Cannot read properties of undefined (reading 'id')错误。这个错误发生在回调函数内部,不在外层try-catch的捕获范围内,也没有被处理,直接导致应用崩溃。

  2. 错误中间件未发挥作用的核心原因
    上述未捕获错误根本没传到错误中间件,自然无法通过中间件返回错误响应。另外多个错误中间件的执行顺序和逻辑也有优化空间——如果前一个中间件没有调用next(error),后续中间件不会执行。


修复方案

1. 处理JWT.verify的错误

把JWT.verify改成可被try-catch捕获的形式,有两种方式:

方案1:使用同步版本

// 替换原JWT.verify代码块
try {
  const decoded = JWT.verify(jwt, 'secret');
  objectid = decoded.id._id;
} catch (jwtErr) {
  next(jwtErr);
  return; // 提前终止,避免后续无效代码执行
}

方案2:Promise封装回调版本

// 先封装JWT.verify为Promise
const verifyJWT = (token, secret) => {
  return new Promise((resolve, reject) => {
    JWT.verify(token, secret, (err, decoded) => {
      if (err) reject(err);
      else resolve(decoded);
    });
  });
};

// 接口中使用await调用
const decoded = await verifyJWT(jwt, 'secret');
objectid = decoded.id._id;

两种方式都能让JWT验证的错误被外层try-catch捕获,再通过next(error)传递给错误中间件。

2. 优化错误中间件逻辑

多个错误中间件需要按优先级处理,只有当前中间件无法处理时,再调用next(error)传递给下一个。比如按错误类型区分返回状态码:

// 优化usererrorFunction:处理用户侧错误
const usererrorFunction = (error, req, res, next) => {
  if (['JsonWebTokenError', 'ValidationError', 'CastError'].includes(error.name)) {
    return res.status(400).json(error.message);
  }
  next(error); // 非用户错误,传递给下一个中间件
};

// 优化errorFunction:处理服务器内部错误
const errorFunction = (error, req, res, next) => {
  return res.status(500).json(error.message || '服务器内部错误');
};

// 调整路由中中间件顺序:先处理用户错误,再处理服务器错误,最后兜底
router.use(usererrorFunction);
router.use(errorFunction);
router.use(regularerrorFunction);

3. 补充async函数错误捕获

接口是async函数,await User.findOne()的catch已经把错误传给next(err),这部分没问题,但要确保所有异步操作的错误都被覆盖。


内容的提问来源于stack exchange,提问作者today

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 09:37:48