如何用中间件处理Express路由错误?为何子路由报错致服务崩溃?
Express子路由异步函数错误未被全局错误中间件捕获的问题
我用Node.js和Express搭建服务器时遇到问题:全局错误处理中间件能处理根路由的同步错误,但子路由异步函数中抛出的错误无法被处理,直接导致服务器崩溃。我不想用try/catch,因为这违背错误处理中间件的设计初衷,请问这是为什么?
相关代码
Server.js
import SubRouter from './routes/SubRouter.js'; import express from 'express'; const server = express(); server.use(express.json()) server.use('/sub', SubRouter); server.get("/update", (req, res) => { throw new Error("Testing") // 此处错误不会导致服务器崩溃 }); // 全局错误处理中间件 server.use((err, req, res, next) => { console.error("Error Handled") res.json({ error: err.message }) }) server.listen(3000, () => { console.log(`listening at 3000 ...`); });
./routes/SubRouter.js
import { Router } from "express"; const SubRouter = Router() SubRouter.get("/update", async (req, res) => { throw new Error("Testing") // 此处错误会导致服务器崩溃 }); export default SubRouter
错误日志
file:///.../routes/SubRouter.js:7 throw new Error("Testing") // This crashes the server ^ Error: Testing at file:///.../routes/SubRouter.js:7:11 at Layer.handle [as handle_request] (/.../node_modules/express/lib/router/layer.js:95:5) at next (/.../node_modules/express/lib/router/route.js:144:13) at Route.dispatch (/.../node_modules/express/lib/router/route.js:114:3) at Layer.handle [as handle_request] (/.../node_modules/express/lib/router/layer.js:95:5) at /.../node_modules/express/lib/router/index.js:284:15 at Function.process_params (/.../node_modules/express/lib/router/index.js:346:12) at next (/.../node_modules/express/lib/router/index.js:280:10) at Function.handle (/.../node_modules/express/lib/router/index.js:175:3) at router (/.../node_modules/express/lib/router/index.js:47:12)
问题原因
核心原因是:Express 4及以下版本不会自动捕获异步函数(标记了async)中抛出的错误。
- 根路由的处理函数是同步函数,Express会自动捕获同步抛出的错误,并调用
next(err)把错误传递给全局错误处理中间件,因此不会崩溃。 - 子路由的处理函数是
async异步函数,抛出的错误会被包装成Promise的reject状态。Express 4及以下默认不会处理未被捕获的Promise rejection,这会触发Node.js的未捕获异常,进而导致服务器崩溃。
解决方法
方法1:使用异步错误捕获包装函数
写一个通用的包装函数,自动捕获异步函数的错误并传递给next:
// 工具函数:捕获异步路由函数的错误 const asyncHandler = (fn) => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); };
然后在子路由中使用该包装函数:
SubRouter.get("/update", asyncHandler(async (req, res) => { throw new Error("Testing"); }));
方法2:升级到Express 5
Express 5版本已经修复了这个问题,会自动捕获异步路由处理函数中抛出的错误,并传递给全局错误处理中间件,无需额外编写包装函数或try/catch。
内容的提问来源于stack exchange,提问作者Manas R. Makde
相关产品推荐
相关产品推荐

