在设计 REST 时,PUT 和 PATCH 是两种用于在服务器上更新资源的 HTTP 方法,但 REST API 中 PUT 与 PATCH 的区别在于它们执行更新的方式以及各自最适用的场景。两种方法都是为了修改现有数据而设计的,但理解 REST API 中的 PUT 和 PATCH 区别可以帮助开发者根据需要执行的更新性质做出最佳选择。在这篇博文中,我们将探讨 REST API 中的 PUT 和 PATCH 区别,重点关注 PATCH 与更新 REST 的讨论、何时使用各自的方法,以及为不同的使用场景选择合适方法的指导。
REST API 中的 PUT 是什么?
要理解 REST API 中 PUT 和 PATCH 的区别,首先需要了解 PUT 的含义。根据 Section 9.6 RFC 2616,REST API 中的 PUT 方法请求将随附的实体存储在所提供的 Request-URI 下。
如果 Request-URI 指向一个已存在的资源,则请求体中包含的实体应被视为源服务器上该资源的更新版本。如果 Request-URI 不指向任何现有资源,但用户代理可以通过该 URI 定义一个新资源,则源服务器可以创建该资源。
In other words, the PUT method is used to update an entire resource on the server. This makes PUT a more “complete” update, often used when a full replacement of the resource is necessary.
PUT 最适合以下场景:
- 更新完整资源(例如用新信息更新用户档案)。
- 替换整个项目或记录。
- 当资源的身份明确,且其数据需要完全刷新时。
In systems like Elasticsearch,幂等操作对于保证数据一致性是必要的。例如,使用 PUT 更新 Elasticsearch 中的文档可以确保相同的请求可以重复执行而不会产生意外的副作用。
REST API 中的 PATCH 是什么?
现在我们已经介绍了 REST APIs 中的 PUT,接下来看一下 REST APIs 中的 PATCH 是什么、它如何工作,然后再比较 REST APIs 中的 PUT 和 PATCH。如 RFC 5789,REST API 中的 PATCH 方法要求将请求实体中描述的一组更改应用到由 Request-URI 标识的资源。
这与 PUT 和 PATCH 的 REST API 区别相符。使用 PATCH 时,你只需发送需要修改的数据,服务器将这些更改应用到现有资源,而不替换整个资源。开发者经常创建补丁来表示这些增量变化,从而确保数据传输最少,更新效率最高。
这就是为什么 REST API 中的 PATCH 方法更适合以下场景:
- 只更新资源中的部分字段(例如,修改用户的邮箱地址或电话号码)。PATCH API 的示例可能只需发送新邮箱,其他字段保持不变。
- 通过减少数据传输量来提升性能。
- 当你需要逐步更新资源,而不是完全替换它时。
- 创建补丁来封装特定字段的更改,比如修改用户邮箱,而不会影响其他无关的数据。
适用于以下平台 headless CMS 这些系统通常要处理复杂的内容结构,使用 PATCH 来做小范围更新(比如修改单个字段)可以减少服务器负载,提升性能。
掌握了这两种方法后,你应该对 REST APIs 中的 PUT 和 PATCH 有了基本了解。不过在深入讨论 REST APIs 中 PUT 与 PATCH 的区别之前,我们需要先理解这两种方法的幂等性。
REST API 中 PUT 与 PATCH 的幂等性
在 REST API 中,幂等性指的是一个操作的属性,当用相同的输入重复执行多次时,结果保持一致。这意味着多次发送相同的请求应该对服务器产生相同的效果,无论执行多少次。这对于确保 API 中的稳定性和可预测性很重要。但它与 REST API 中 PUT 和 PATCH 的区别有什么关联呢?
PUT 方法和幂等性
REST API 中的 PUT 方法始终是幂等的,因为它设计用来用请求中提供的数据替换指定 URI 处的整个资源。换句话说,如果你用相同的资源数据多次执行 PUT 请求,结果始终相同。
为什么 PUT 是幂等的?发送 PUT 请求时,你在告诉服务器:「这是我希望这个资源具有的完整且精确的状态。」无论你发送一次还是多次 PUT 请求,资源的最终状态始终相同。
比如说,你要更新用户的邮箱地址。如果多次发送同一个 PUT 请求,结果不会改变,因为每次都是用相同的数据替换该资源。
Example:
| PUT /users/1{"username": "john_doe","email": "[email protected]"} |
多次发送这个请求,结果始终一致:
| {"username": "john_doe","email": "[email protected]"} |
即使用户的数据已经是这样,再次发送请求也不会改变任何内容。它用相同的数据替换现有数据,所以无论重复请求多少次,结果都保持一致。这就是 PUT 的幂等性。
PATCH 方法和幂等性
REST API 中的 PATCH 方法通常也是幂等的,但灵活性更高。创建补丁时,确保操作是幂等的(例如设置一个值),以避免重复请求产生意外的副作用。PATCH 是否幂等取决于具体的操作和被修改的数据。
为什么 PATCH 是幂等的?对于 PATCH 请求,幂等性意味着多次应用相同的补丁会产生相同的结果。只要补丁本身在重复应用时不会产生额外的副作用或更改,这就成立。如果你持续使用相同的数据应用相同的补丁,结果在第一次应用后应该保持不变。
例如,如果你只是更新用户的邮箱,重复发送相同的 PATCH 请求在第一次之后不会改变结果,即使请求被发送多次。用户的邮箱将保持不变,资源的状态也不会改变。
PATCH API 示例:
| PATCH /users/1{"email": "[email protected]"} |
如果邮箱已经是 [email protected],再次应用这个 PATCH 不会产生任何变化,这样就是幂等的。
不过,在某些情况下 PATCH 也可能不是幂等的。比如,如果 PATCH 操作修改计数器或向列表添加项目(比如递增数字或追加到数组),多次应用同一个 PATCH 可能会产生不同的结果。这样就违反了幂等性原则。
非幂等 API REST PATCH 示例:
| PATCH /counter/1{"increment": 1} |
如果你多次应用这个 PATCH,计数器会持续增加,每次的结果都不同。这不是幂等的,因为每次应用的结果都会改变。
掌握了基础概念后,我们来看几个具体场景,更好地理解 REST API 中 PUT 和 PATCH 的区别。
示例场景:REST API 中的 PUT 与 PATCH
理论讲够了,我们来看看 PUT 和 PATCH 的实际区别。通过例子来说明,既包括幂等操作,也包括非幂等操作。
场景 1:PUT 请求 – 替换整个资源
假设有一个 API 端点用于管理电子商务系统中的产品。你需要更新产品的所有详细信息,包括名称、价格和描述。这是 HTTP 方法 PUT 与 PATCH 的典型示例,其中 PUT 用于替换整个产品资源。
Initial Product:
| GET /products/1001{"id": 1001,"name": "Laptop","price": 999.99,"description": "A powerful laptop."} |
你需要更新产品的价格和描述。发送一个包含完整资源的 PUT 请求:
PUT Request:
| PUT /products/1001{"id": 1001,"name": "Laptop","price": 899.99,"description": "A discounted powerful laptop."} |
如果你执行一次或多次 PUT 请求,结果始终相同。产品详情会更新以反映新价格和描述,每次都会产生相同的结果。这保证了 PUT 的幂等性。
结果(PUT 请求后):
| {"id": 1001,"name": "Laptop","price": 899.99,"description": "A discounted powerful laptop."} |
场景 2:PATCH 请求 - 更新资源的部分内容
现在考虑用 PATCH 请求只更新产品详情的一部分,比如描述。PATCH 请求示例:如果你想改变产品描述但不改变价格,PATCH 是更好的选择。实现这一点需要创建只包含需要修改字段的补丁,比如在这个 PATCH 请求示例中的描述字段。
Initial Product:
| GET /products/1001{"id": 1001,"name": "Laptop","price": 999.99,"description": "A powerful laptop."} |
PATCH Request:
| PATCH /products/1001{"description": "A lightweight, powerful laptop."} |
发送此 PATCH 请求时,只有描述字段会被更新。如果你多次发送相同的 PATCH 请求,描述在首次更新后会保持不变,使该请求具有幂等性。
结果(PATCH 请求后):
| {"id": 1001,"name": "Laptop","price": 999.99,"description": "A lightweight, powerful laptop."} |
如果再次应用相同的 PATCH,产品描述仍会与首次 PATCH 后相同。结果保持一致,这使得 PATCH 在此场景中具有幂等性。
场景 3:PATCH 请求 - 非幂等操作
现在看一个非幂等的 PATCH 操作。假设有一个用户钱包余额的端点,你想增加余额。PUT 和 PATCH 的差异可以在这里说明:PATCH 每次都会向余额添加一个值。
Initial Wallet:
| GET /users/1/wallet{"id": 1,"balance": 100.00} |
PATCH Request:
| PATCH /users/1/wallet{"increment": 50.00} |
此 PATCH 请求会将钱包余额增加 50 美元。如果多次发送此 PATCH 请求,余额每次都会增加,每次都产生不同的结果。这是非幂等的,因为重复应用相同的 PATCH 会导致不同的结果。
结果(首次 PATCH 请求后):
| {“id”: 1,”balance”: 150.00} |
结果(第二次 PATCH 请求后):
| {“id”: 1,”balance”: 200.00} |
这里 PATCH 不是幂等的,因为用相同数据的重复请求产生不同的结果。
回顾:REST API 中 PUT 与 PATCH 的主要区别
PUT 和 PATCH 的关键区别在于如何处理更新。PUT 替换整个资源,所以任何缺失的字段都会被删除,可能导致数据丢失。这就是开发者使用补丁进行部分更新的原因,以避免覆盖未更改的字段并保持数据完整性。
这使 PATCH 在部分更新时更高效。一个 PATCH 示例是,如果你只想更新用户的电子邮件,创建补丁只会发送电子邮件字段,而不像 PUT 请求那样需要整个用户数据。
使用 PUT 时,需要完整的数据有效负载。这意味着必须发送每个字段,任何缺失的字段都会被删除。而 PATCH 只发送需要更新的字段,效率更高,特别是对于只有少量更改的大型资源。PATCH 方法允许更专注和更小的请求,减少数据传输。
PUT 和 PATCH 都是幂等的。这意味着重复相同的请求会产生相同的结果。但是 PUT 更可预测,因为它替换整个资源。PATCH 在仅修改指定字段时,可能会根据服务器处理更新的方式导致不同的结果。
例如,如果 PATCH 增加一个计数器,重复请求可能导致不同的结果。不过,PATCH 操作也可以设计为幂等的。
总之,PUT 和 PATCH 的区别归结为更新的性质:PATCH 用于部分更改,而 PUT 用于完整资源替换。两者都是幂等的,但 PATCH 可能更复杂。
Final Thoughts
PUT 和 PATCH 都是 API 设计中的基础方法,但它们服务于不同的目的,这正是 PUT 和 PATCH 区别的本质。通过理解 PUT 和 PATCH 在本文中所涵盖的示例之间的区别,你可以显著提高 API 的效率、清晰度和功能性。通过根据你的具体情况选择正确的方法(PATCH 与更新),你可以使 API 更高效、更易维护、更友好。始终考虑更新的性质(完整还是部分),以及对性能和数据完整性的影响,选择最符合你需求的方法。