HTTP 方法 PUT 意为在某个给定 URL 上进行资源的创建或替换。
例如,可以想象文件。如果你将一个文件上传到 S3 的某个 URL,你希望在该 URL 创建文件,或者如果文件已存在,则替换现有文件。这就是 PUT。
现在,假设一个 Web 应用程序有一个 `Invoice` 模型,其中包含一个 `paid` 标志,用于指示发票是否已支付。如何以 RESTful 的方式设置此标志?通过 PUT 向 `/invoices/:id` 提交 `paid=1` 不符合 HTTP 语义,因为此类请求不会发送发票的完整表示进行替换。
在 GET、POST、PUT、DELETE 方法的约束下,传统的做法是将给定发票的 `paid` 标志定义为资源本身。因此,你需要定义一个路由,以便能够向 `/invoices/:id/paid` PUT `paid=1`。你必须这样做,因为 PUT 不允许对资源进行部分更新。
现在,我们来看看典型的 Ruby on Rails 应用程序中的普通编辑表单。我们发送完整表示进行替换的次数有多少?不总是,也许可以说实际上很少这样做。例如,传统的 `created_at` 和 `updated_at` 时间戳通常不能由最终用户设置,尽管它们通常被认为是映射到记录的资源的表示的一部分。
此外,PUT 是一个幂等方法。你应该能够根据需要重放请求任意多次,并获得相同的资源,这一点有时会被使用嵌套属性更新父资源来创建子资源的传统惯用法所违反。
理论上没有任何东西阻止 PUT 进行部分更新,但在 HTTP 标准化时,替换语义已经部署。
因此,在 1995 年定义了 PATCH 方法,后来进行了标准化。PATCH 方法不安全,也不幂等,并且允许完全和部分更新以及对其他资源的副作用。
实际上,正如你所见,对于更新资源,PATCH 比 PUT 更适合日常 Web 编程。在 Ruby on Rails 中,它自然对应于我们使用 `update_attributes` 来更新记录的方式。
因此,在 Rails 4.0 中,PATCH 将成为更新资源的首选方法。
这是一个重要的更改,但我们计划以向后兼容的方式进行。
例如,当在 `config/routes.rb` 中声明一个资源时,
resources :users
在 Rails 4.0 中,`UsersController` 中更新用户的操作仍然是 `update`。
在 Rails 4.0 中,向 `/users/:id` 发送的 PUT 请求仍然像今天一样路由到 `update`。因此,如果你有一个接收真实 PUT 请求的 API,它将正常工作。
然而,在 Rails 4.0 中,路由器还将向 `/users/:id` 发送的 PATCH 请求路由到 `update` 操作。
因此,在 Rails 4.0 中,PUT 和 PATCH 都将被路由到 `update`。
已持久化资源的表单
form_for @user
在隐藏字段 “_method” 中获取 “patch”。RFC 在表示 PATCH 请求中的更改方式上故意含糊不清。提交表单是完全有效的,客户端和服务器只需就更新资源的接受方式达成一致即可。
我想强调 “_method” 技巧是为了解决 Web 浏览器的局限性。你可能知道 Rails 会路由真实的 HTTP 方法。也就是说,实际的 PUT、DELETE,现在还有 PATCH 请求都会被路由到它们各自的操作。
PATCH 请求在所有地方都可用,就像今天其他方法一样。路由 DSL 有一个 `patch` 宏,`:via` 理解 `:patch` 符号。测试可以发出 PATCH 请求,请求对象响应 `patch?` 等。请参阅原始提交了解详情(以及一个重要的后续这里)。
是的,应该会。我亲自尝试了 Apache、nginx、Phusion Passenger、Unicorn、Thin 和 WEBrick。它们开箱即用都能理解 PATCH 请求。
此外,HTTP 客户端通常应该能够发出 PATCH 请求。例如,在 curl(1) 中,你会执行
curl -d'user[name]=wadus' -X PATCH https://:3000/users/1
我们要感谢 David Lee 的贡献以及他即使在几个月的讨论之后仍然对此保持兴趣的无尽耐心。
此外,我想强调补丁本身的质量。它非常出色:代码、测试、所有文档都已修订,代码中的注释也已修订。一切都经过仔细而彻底的更新。一个典范性的补丁。