我的博客

Express 5 升级要注意的几个变化

Express 5 在拖了将近十年之后终于正式发布。大部分 API 保持不变,但有几处改动会让老代码直接报错。下面是我升级几个项目时遇到的问题。

1. 异步错误终于能自动捕获了

这是最值得升级的理由。Express 4 中,async 函数里抛出的异常不会进入错误处理中间件,要么自己 try/catch,要么装 express-async-errors。

Express 5 中,路由处理函数返回的 Promise 被 reject 时,会自动调用 next(err):

app.get('/user/:id', async (req, res) => {
  const user = await db.getUser(req.params.id); // 抛错会自动进入错误处理中间件
  res.json(user);
});

升级后可以删掉 express-async-errors 这类补丁。

2. 路由路径语法变了

Express 5 使用新版的 path-to-regexp,有几个写法不再支持:

// 通配符必须命名
app.get('/*', handler);          // Express 4
app.get('/*splat', handler);     // Express 5

// 可选参数改用花括号
app.get('/user/:id?', handler);  // Express 4
app.get('/user{/:id}', handler); // Express 5

字符串路径里也不能再写正则字符(如 (、)、+),需要正则时直接传 RegExp 对象。

3. req.body 默认是 undefined

在 Express 5 中,如果请求体没有被任何解析中间件处理(比如 GET 请求,或 Content-Type 不匹配),req.body 是 undefined,而不是空对象。

const { name } = req.body; // 可能直接报错

可以加一个兜底中间件,或者在使用处写成 req.body ?? {}。

4. urlencoded 的 extended 默认为 false

express.urlencoded() 不传参数时,默认使用简单的解析方式,a[b]=1 不会再被解析成嵌套对象。需要嵌套结构的话要显式指定 extended: true。

5. req.query 变成了只读

req.query 现在是一个 getter,不能直接赋值修改。一些"在中间件里清洗 query 参数再写回去"的写法会失效,需要改成把结果放到别的属性上,比如 req.validatedQuery。

6. 删除的 API

已删除 替代
app.del() app.delete()
res.redirect('back') res.redirect(req.get('Referrer') || '/')
res.send(200) res.sendStatus(200)
res.json(obj, 200) res.status(200).json(obj)
req.param('name') req.params / req.body / req.query

7. 最低需要 Node.js 18

如果服务器上的 Node 版本还很老,先升级 Node。

升级建议

  1. 全局搜索 '*'、:\w+? 这类路由写法,按新语法修改;
  2. 搜索 req.body 的解构,确认有兜底;
  3. 跑一遍完整的测试或手动点一遍主要流程;
  4. 删掉不再需要的异步错误补丁。

对于中小项目,整个升级通常一两个小时就能完成。