介绍 Express v5:Node.js 框架的新时代
十年前(2014 年 7 月),Express v5 发布拉取请求被打开,现在终于被合并并发布了!
我们感谢所有贡献者的工作,特别是 Doug Wilson,他过去十年一直致力于确保 Express 是最稳定的项目。没有他以及许多其他人的贡献,这次发布就不可能实现。
八个月前,我们公开了一项计划,旨在推动 Express 发展。该计划包括重新承诺多年前概述的治理,并增加更多贡献者来启动进展。许多人可能没有意识到,健全的项目治理对于一个大型开源项目的健康至关重要。我们感谢 OpenJS 基金会跨项目委员会及其成员帮助我们制定了这项计划。
那么 v5 呢?
这次发布旨在“无聊”!这听起来可能很奇怪,但我们有意保持简单,以解除生态系统的阻塞,并为未来版本中更具影响力的变化提供可能。这也在向 Node.js 生态系统发出信号,表明 Express 正在重新行动起来。本次发布的重点是停止对旧 Node.js 版本的支持,解决安全问题,并简化维护。
在深入探讨本次发布中的变化之前,我们先解释一下为什么它是在 next
dist-tag 上发布 v5 的。作为项目复兴的一部分,我们成立了一个安全工作组和安全分类团队,以应对开源供应链安全日益增长的需求。我们进行了一次安全审计(更多细节即将公布),发现了一些需要解决的问题。因此,除了在公共问题中进行的“正常”工作之外,我们还在私人分支中进行了大量的安全工作。这项安全工作在发布时需要协调,以确保代码和 CVE 报告同时发布。您可以在我们的安全发布说明中找到最新修补漏洞的摘要。
虽然我们未能同时发布 v5、这篇博客文章、更新日志和文档,但我们认为拥有一个安全稳定的发布最为重要。
我们将尽快提供有关我们的长期支持 (LTS) 计划的更多详细信息,包括何时将版本从 next
移动到 latest
。目前,如果您不习惯使用最前沿的版本(即使它相当稳定),那么您应该等到版本被标记为 latest
后再升级。尽管如此,我们期待与您合作,解决您在升级过程中遇到的任何错误。
突破性变更
v5 版本具有最少量的突破性变更,按对应用程序的影响程度在此处列出。
还有一些细微的变化:有关详细信息,请参阅迁移到 Express 5。
终止对旧 Node.js 版本的支持
再见 Node.js 0.10,你好 Node 18 及更高版本!
此版本取消了对 v18 之前 Node.js 版本的支持。这是一个重要的变化,因为支持旧的 Node.js 版本一直阻碍着许多关键的性能和可维护性改进。此更改还支持更稳定和可维护的持续集成 (CI),采用新的语言和运行时特性,并取消不再需要的依赖项。
我们认识到这可能会给一些拥有旧的或“停滞”应用程序的企业带来困难,因此我们正在与 HeroDevs 合作,提供“永续支持”,即使在 v4 终止生命周期后,仍将包括关键安全补丁(有关这些计划的更多信息即将发布)。尽管如此,我们强烈建议您尽快更新到现代 Node.js 版本。
路径匹配和正则表达式的更改
v5 版本将 [email protected]
更新为 [email protected]
,其中包含了多年的变化。如果您曾使用任何 5.0.0-beta 版本,请注意最后一刻的更新,该更新极大地改变了路径语义,以消除任何 ReDoS 攻击的可能性。有关更详细的更改,请参阅 path-to-regexp
的 README。
不再支持正则表达式
此版本不再支持“子表达式”正则表达式,例如 /:foo(\\d+)
。这是一个常用的模式,但我们出于安全原因将其移除。不幸的是,很容易编写出在解析输入时具有指数时间行为的正则表达式:即可怕的正则表达式拒绝服务 (ReDoS) 攻击。这很难预防,但作为一个将字符串转换为正则表达式的库,我们有责任处理这些安全方面的问题。
如何迁移:防止 ReDoS 攻击的最佳方法是使用健壮的输入验证库。npm 上有许多,具体取决于您的需求。TC 成员 Wes Todd 维护着一个基于中间件的“代码优先”OpenAPI 库,用于处理此类问题。
通配符、可选参数和捕获
此版本包含了对常见路由模式的简化模式。随着正则表达式语义的移除,您的路由编写方式也发生了一些细微但有影响的更改。
:name?
变为{:name}
。在路由的可选部分使用{}
意味着您现在可以执行/base{/:optional}/:required
之类的操作,并且哪些部分是可选的也更加明确。*
变为*name
。- 新的保留字符:
(
、)
、[
、]
、?
、+
和!
。这些字符已被保留,以便为未来的改进留出空间,并防止在迁移时出现错误,因为这些字符在以前的版本中具有特定含义。
命名所有参数
此版本不再支持有序数字参数。
在 Express v4 中,您可以使用正则表达式捕获组获取数字参数(例如,/user(s?)
=> req.params[0] === 's'
)。现在所有参数都必须命名。除了要求命名外,Express 现在支持所有有效的 JavaScript 标识符或带引号的标识符(例如,/:"this"
)。
Promise 支持
这一点可能有点争议,但我们“保证”我们正朝着正确的方向前进。我们增加了对中间件中引发的错误返回被拒绝的 Promise 的支持。这不包括从返回的已解决的 Promise 中调用 next
。旧的 Express 应用程序中存在许多对 Promise
行为有预期的边缘情况,在我们能够奔跑之前,我们需要先学会走路。对于大多数人来说,这意味着您现在可以编写如下所示的中间件:
app.use(async (req, res, next) => {
req.locals.user = await getUser(req);
next();
});
请注意,此示例使用 async/await
,并且 getUser
调用可能会抛出错误(例如,用户不存在,用户数据库宕机等),但如果成功,我们仍然会调用 next
。如果我们要依赖错误处理中间件,我们不再需要在线捕获错误,因为路由器现在将捕获被拒绝的 Promise,并将其视为调用 next(err)
。
注意:最佳实践是在尽可能靠近发生错误的地方处理错误。因此,虽然现在在路由器中处理,但最好在中间件中捕获错误并进行处理,而无需依赖单独的错误处理中间件。
Body parser 变更
body-parser
有一些变化:
- 添加选项以自定义 urlencoded body 深度,默认值为 32,作为针对 CVE-2024-45590 的缓解措施(技术细节)
- 移除已弃用的
bodyParser()
组合中间件 req.body
不再总是初始化为{}
urlencoded
解析器现在将extended
默认为 false- 新增对 Brotli 无损数据压缩的支持
移除已弃用的方法签名
Express v5 移除了许多已弃用的方法签名,其中许多是从 v3 继承下来的。以下是您需要进行的更改:
res.redirect('back')
和res.location('back')
:不再支持魔术字符串'back'
。请明确使用req.get('Referrer') || '/'
代替。res.send(status, body)
和res.send(body, status)
签名:请使用res.status(status).send(body)
。res.send(status)
签名:对于简单的状态响应,请使用res.sendStatus(status)
;对于发送带可选主体的状态码,请使用res.status(status).send()
。res.redirect(url, status)
签名:请使用res.redirect(status, url)
。res.json(status, obj)
和res.json(obj, status)
签名:请使用res.status(status).json(obj)
。res.jsonp(status, obj)
和res.jsonp(obj, status)
签名:请使用res.status(status).jsonp(obj)
。app.param(fn)
:此方法已被弃用。请直接通过req.params
访问参数,或根据需要使用req.body
或req.query
。app.del('/', () => {})
方法:请改用app.delete('/', () => {})
。req.acceptsCharset
:请使用req.acceptsCharsets
(复数形式)。req.acceptsEncoding
:请使用req.acceptsEncodings
(复数形式)。req.acceptsLanguage
:请使用req.acceptsLanguages
(复数形式)。res.sendfile
方法:请改用res.sendFile
。
作为一个框架,我们的目标是确保 API 尽可能保持一致。我们已移除这些已弃用的签名,以使 API 更具可预测性且更易于使用。通过简化每个方法以使用单一、一致的签名,我们简化了开发人员的体验并减少了混淆。
迁移和安全指南
对于希望从 v4 迁移到 v5 的开发人员,我们提供了详细的迁移指南,以帮助您了解这些变化并确保顺利升级。
此外,我们一直致力于制定全面的威胁模型,以阐明我们“快速、不固执己见、极简的 Node.js Web 框架”的理念。它提供了关于用户输入验证和安全实践等关键领域的见解,这些对于在应用程序中安全使用 Express 至关重要。
我们的工作才刚刚开始
我们认为 v5 版本的发布是 Express 生态系统朝着为公司、政府、教育者和业余项目提供稳定可靠工具迈出的里程碑。作为 Express 项目的新管理者,我们承诺秉持这一目标推动生态系统向前发展。如果您希望支持我们这项志愿工作,请考虑通过我们的赞助机会支持该项目及其维护者。
我们有一个庞大的待办任务、拉取请求和 Express 及其依赖项问题列表。自然,我们希望开发人员将继续报告问题以补充此待办事项,并提出新的拉取请求,我们将继续与社区合作,对它们进行分类和解决。我们期待继续改进 Express,使其对全球用户有用。
编辑此页面