express控制器级中间件需显式传入路由方法,形参必须为(req, res, next)且顺序固定;挂载于router.get()等具体路由参数中而非app.use();须调用next()才能执行后续处理器;req/res对象共享引用,可安全添加属性;ts中需扩展express.request接口并显式标注requesthandler类型。

Express 中间件函数怎么写才被识别为控制器级中间件
Express 不会自动把普通函数当作控制器中间件——它必须被显式传入路由方法,且位置在 req、res、next 参数签名正确的情况下才生效。
- 函数必须接收三个参数:
req、res、next(顺序不能错,少一个或类型不对都会跳过执行) - 不能直接
app.use(middleware),那是全局中间件;控制器中间件要塞进路由定义里,比如router.get('/user', authMiddleware, getUserHandler) - 如果中间件内部没调用
next(),后续处理器(包括控制器函数)就永远不会执行——常见于忘记加next()或条件分支里漏掉 - 异步操作后必须手动调用
next(),不能依赖 return;用async/await时建议包一层try/catch并在 catch 里next(err)
如何让中间件只对某个控制器生效,而不是整个路由路径
关键在挂载位置:不是挂到 router.use() 或 app.use(),而是作为回调函数之一,紧贴在对应路由方法的参数列表中。
-
router.get('/order/:id', validateOrderId, loadOrder, renderOrder)—— 这三个函数都是该路由独享的,前两个是中间件,最后一个是最终控制器 - 避免误用
router.use('/order', someMiddleware):这会让所有以/order开头的路径(如/order/list、/order/123)都经过它,不是“仅控制器级” - 如果想复用但又限定范围,推荐封装成高阶函数,比如
withAuth('admin')返回具体中间件,再按需传入各路由 - 注意路径匹配优先级:Express 按注册顺序执行,同路径下多个中间件按参数顺序依次执行,靠前的
next()不调用,后面的全被跳过
中间件里修改 req 或 res 后,控制器还能拿到吗
能。Express 的 req 和 res 是同一个对象实例,在中间件和控制器之间共享引用,改了就真改了。
- 常见做法:中间件解析 token 后挂
req.user = { id, role },控制器直接读req.user.id - 别在中间件里重写
res.send或res.json除非你清楚后果——这会影响控制器对响应的控制权,容易导致重复发送或响应中断 - 如果中间件决定终止流程(如鉴权失败),应该调用
res.status(403).json({ error: 'forbidden' })然后return next()或直接return,但必须确保不再调用next(),否则控制器还会执行 - 修改
req.query或req.body要小心:有些 body-parser 中间件已冻结这些对象,直接赋值可能无效,建议用Object.assign(req.query, {...})或新建属性
用 TypeScript 写控制器中间件时类型怎么对得上
TypeScript 不会自动推导中间件类型,RequestHandler 类型必须显式标注,否则 req 上自定义字段(如 req.user)在控制器里会报错。
- 先扩展 Express 的
Request接口:declare global { namespace Express { interface Request { user?: { id: string; role: string }; } } } - 中间件函数写法:
const authMiddleware: RequestHandler = (req, res, next) => { ... },不能省略类型注解 - 如果中间件带参数(如
roleRequired('admin')),返回类型应为RequestHandler,而非普通函数 - VS Code 有时缓存类型,改完
global.d.ts后需要重启 TS Server(Ctrl+Shift+P → “TypeScript: Restart TS server”)
next() 忘调、路径挂载点选错、TS 类型没补全——这几个地方卡住,比逻辑写错还难调试。










