如何在 Express 中统一包装异步请求处理器以安全捕获错误

本文介绍一种可靠方式,为 express 的所有异步路由处理器自动添加 try-catch 包装,避免因未处理 promise 拒绝(如 mongodb 报错)导致进程崩溃,并确保错误能正确传递至全局错误中间件。

Express 默认不会自动捕获异步函数中抛出的错误(例如 async 处理器内部 await 一个失败的 P

romise),因为这类错误会以未处理的 Promise rejection形式存在,根本不会进入同步 try...catch 块——这正是你遇到“catch 不生效、应用直接崩溃”的根本原因。

你的 tryCatch 包装器逻辑方向正确,但当前实现存在关键缺陷:它同步调用了 func(req, res, next),而该函数返回的是一个 Promise;你并未 await 它,因此 Promise 内部的异常无法被同步 catch 捕获。

✅ 正确写法如下(支持 TypeScript 类型,兼容 Express 4.x+):

import { Request, Response, NextFunction } from 'express';

export const tryCatch = (
  func: (req: Request, res: Response, next: NextFunction) => Promise
): ((req: Request, res: Response, next: NextFunction) => void) => {
  return (req, res, next) => {
    // 关键:必须 await 并 catch Promise rejection
    func(req, res, next).catch(next);
  };
};

⚠️ 注意事项:

  • func 必须是 async 函数(即返回 Promise),否则 .catch() 无意义;
  • 不要手动调用 next(err) 在 catch 块里再做判断——直接 func(...).catch(next) 是最简洁、最健壮的方式,Express 会自动识别并交由后续错误中间件处理;
  • 确保你已定义并注册了全局错误处理中间件(必须有 4 个参数),例如:
// 全局错误处理器(务必放在所有路由和普通中间件之后)
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error('Unhandled error:', err);
  res.status(500).json({ success: false, message: 'Internal Server Error' });
});

? 使用示例:

// routes/auth.ts
import { tryCatch } from '../utils/tryCatch';

export const login = tryCatch(async (req, res) => {
  const user = await User.findOne({ email: req.body.email });
  if (!user) throw new Error('User not found'); // ✅ 被自动捕获
  const isValid = await bcrypt.compare(req.body.password, user.password);
  if (!isValid) throw new Error('Invalid credentials');
  res.json({ token: signToken(user) });
});

? 进阶建议:
若项目中大量使用 async/await,还可封装成更通用的高阶函数(如支持返回值类型推导),或结合日志、错误分类(如 MongoError 判定后转为 409)、响应标准化等能力进一步增强鲁棒性。但核心原则不变:对 async 处理器,必须 .catch(next),而非仅包裹同步调用