先记住这个答案
app.all()是路由方法,路径必须精确匹配,但可捕获任意HTTP方法;app.use()是中间件挂载,路径视为前缀,方法一律通过。所以若对同一精确URL的GET/POST/PUT等需要相同前置处理,用all;若想对某个目录所有请求做统一处理,用use。all不自动调用next,use必须显式调用next才能放行。
- all按完整路径匹配所有HTTP方法
- use按前缀匹配且不分方法
- 端点级统选用all,通用中间件用use
方法匹配与路径匹配规则拆分
app.all()在Express中属于路由方法,其注册位置在路由表中,匹配逻辑与app.get()相同:请求路径必须与给定模式完全匹配,模式支持字符串、正则和参数。区别仅在于方法集合被扩展为http模块列出的所有方法,因此POST /secret也会进入app.all('/secret', fn)。
app.use()用于添加中间件,它的第一个路径参数是挂载前缀:请求只要以该前缀开头就会调用中间件,即使路径更深。如果确实没有第二个参数,默认前缀为/,所有请求都会经过。它从不检查请求方法,因此原本给PUT /user的请求也可能被app.use('/user', fn)拦截。Express在内部把use的路径转换成正则前缀,和路由的精确匹配存在本质差别。
精确端点跨方法的鉴权与前置处理
一个订单服务有/api/orders/:id,同时支持GET(查询)、PUT(整体更新)和DELETE(删除)。这三种操作都需要先验证Authorization头、解析req.params.id并做存在性检查。如果分别写三次重复代码,后续增加PATCH时极易遗漏。采用app.all('/api/orders/:id', authMiddleware, orderLoader)能在该精确路径统一挂载前置逻辑,且不影响/api/orders列表端点。
若有人改用app.use('/api/orders/:id', authMiddleware),问题立刻出现:路径前缀使/api/orders/123/items这类子资源也会被拉进鉴权,但业务上该子资源可能另有一套权限规则。鉴权逻辑被迫去解析完整路径,否则会产生越权。因此端点级统一处理必须使用all,而不是use,二者的边界在于是否需要“完全匹配”这一前提。
误判前缀与完整路径的代价
app.all不具备前缀通配能力。在Express 4中若想匹配/secret下所有子路径,必须写app.all('/secret/*', handler),而且GET /secret/child会命中,但GET /secret本身不命中。忘记加星号会漏掉子路径,导致部分请求直接落到后续路由或404。Express 5使用path-to-regexp的新语法,通配符改为/*splat,升级队列必须同步检查所有all写法。
app.use的错误是过度匹配。app.use('/secret')会同时处理/secret、/secret/123甚至/secret/anything/deep,且方法不限。若内部逻辑只针对GET而没检查req.method,就会意外处理DELETE,引发副作用。正确处理是:在use回调开头判断方法并用next()跳过;或者改回all并补上精确路径。盲目使用use总会扩大处理范围,排障时先检查注册顺序与路径参数。
容易答错的地方
- all会匹配所有子路径
- 常见误解是
app.all('/user')会拦截/user/1。它只会匹配/user本身。若要子路径,应使用/user/*或正则。Express官方文档明确说明路由路径与get相同,都是精确匹配端点。 - use不指定路径等同于all
app.use(fn)与app.all('*', fn)不等价。use默认以/为前缀且调用链始终进入,而all('*')需要路径模式,且在Express 4中*匹配任意路径但同样不区分方法,但写法与执行位置不同。use通常负责解析请求体等早期工作,all更适合路由-方法决策。
面试官还会怎么问?
app.all 可以放在 app.use 之前注册吗?顺序有什么影响?
可以。中间件链条按注册顺序执行,先注册的先被调用。若app.all在app.use之前,请求先经过all,若它不调用next(),后续use不会执行;顺序颠倒也一样。需要根据预期决定谁先做日志、谁做鉴权。
app.all 是否等同于 app.use 加方法判断?
不完全等价。app.all要求路径完整匹配,而use是前缀匹配,因此仅当路径精确一致且后端方法判断为所有http方法时才等价,但写法冗长且容易漏改。更本质的是all属于路由回调,调用顺序与错误处理规则同普通路由,而use是中间件,错误语义略有不同。
Express 5 中 app.all 和 app.use 的行为有变化吗?
路径匹配底层改用了path-to-regexp 8,通配符语法从*改为/*splat,但all与use的核心语义不变:all仍要求完整路径而use仍按前缀。升级时要检查所有带*或正则的路径写法,避免匹配范围变化。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。