REST API 中 HTTP PUT 與 HTTP PATCH 的區別

REST,Spring MVC
Remote
0
12:52 PM · Dec 01 ,2025

1. 概述

在本快速教程中,我們將探討 HTTP PUT 和 PATCH 請求方法之間的差異,以及這兩種操作的語義。

我們將使用 Spring 實現兩個 REST 端點,以支持這兩種操作,從而更好地理解它們的差異以及正確的使用方法。

2. 何時使用 PUT 以及何時使用 PATCH?

我們先從一個簡單和一個稍複雜的陳述開始。

當客户端需要完全替換現有資源時,他們可以使用 PUT。當他們進行部分更新時,他們可以使用 HTTP PATCH。

例如,當更新資源的單個字段時,發送完整的資源表示形式可能既繁瑣又浪費了大量不必要的帶寬。在這種情況下,PATCH 的語義更合理。

這裏需要考慮的重要方面是 冪等性。PUT 是冪等的;PATCH 可以是冪等的,但並非必需。因此,根據我們正在實施的操作的語義,我們可以根據這一特性選擇一個或另一個。

3. 實現 PUT 和 PATCH 邏輯

假設我們要實現更新 HeavyResource資源的 REST API,並且需要更新多個字段:

public class HeavyResource {
    private Integer id;
    private String name;
    private String address;
    // ...
}

首先,我們需要創建處理資源完整更新的端點,使用 PUT 方法:

@PutMapping("/heavyresource/{id}")
public ResponseEntity<?> saveResource(@RequestBody HeavyResource heavyResource,
  @PathVariable("id") String id) {
    heavyResourceRepository.save(heavyResource, id);
    return ResponseEntity.ok("resource saved");
}

這是一個標準的更新資源的端點。

現在假設地址字段經常由客户端更新。在這種情況下,我們不希望發送整個 HeavyResource對象以及所有字段,但我們仍然希望能夠僅更新 address字段——通過 PATCH 方法。

我們可以創建一個 HeavyResourceAddressOnlyDTO 來表示地址字段的局部更新:

public class HeavyResourceAddressOnly {
    private Integer id;
    private String address;
    
    // ...
}

接下來,我們可以利用 PATCH 方法發送局部更新:

@PatchMapping("/heavyresource/{id}")
public ResponseEntity<?> partialUpdateName(
  @RequestBody HeavyResourceAddressOnly partialUpdate, @PathVariable("id") String id) {
    
    heavyResourceRepository.save(partialUpdate, id);
    return ResponseEntity.ok("resource address updated");
}

通過這種更細粒度的 DTO,我們可以只發送需要更新的字段,而無需發送整個 HeavyResource

如果有很多這些局部更新操作,我們還可以跳過為每個操作創建自定義 DTO,只使用一個 map:

@RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> partialUpdateGeneric(
  @RequestBody Map<String, Object> updates,
  @PathVariable("id") String id) {
    
    heavyResourceRepository.save(updates, id);
    return ResponseEntity.ok("resource updated");
}

這個解決方案將為我們提供更大的 API 實現靈活性,但我們也會失去一些東西,例如驗證。

4. 測試 PUT 和 PATCH

最後,我們來編寫測試,用於這兩個 HTTP 方法。

首先,我們想通過 PUT 方法測試資源的完整更新:

mockMvc.perform(put("/heavyresource/1")
  .contentType(MediaType.APPLICATION_JSON_VALUE)
  .content(objectMapper.writeValueAsString(
    new HeavyResource(1, "Tom", "Jackson", 12, "heaven street")))
  ).andExpect(status().isOk());

通過使用 PATCH 方法可以實現部分更新的執行:

mockMvc.perform(patch("/heavyrecource/1")
  .contentType(MediaType.APPLICATION_JSON_VALUE)
  .content(objectMapper.writeValueAsString(
    new HeavyResourceAddressOnly(1, "5th avenue")))
  ).andExpect(status().isOk());

我們還可以編寫一個更通用的測試:

HashMap<String, Object> updates = new HashMap<>();
updates.put("address", "5th avenue");

mockMvc.perform(patch("/heavyresource/1")
    .contentType(MediaType.APPLICATION_JSON_VALUE)
    .content(objectMapper.writeValueAsString(updates))
  ).andExpect(status().isOk());

5. 使用 Null 值處理部分請求當我們編寫 PATCH 方法的實現時,我們需要指定如何處理在 HeavyResourceAddressOnly 中收到 Null 值作為 address 字段值的方案。

假設客户端發送以下請求:

{
   "id" : 1,
   "address" : null
}

那麼我們可以將其處理為將 address 字段的值設置為 null 或通過將其視為無更改來忽略此類請求。

我們應該為處理 null 選擇一種策略,並在每個 PATCH 方法實現中堅持使用該策略。

6. 結論

在本文中,我們重點介紹了 HTTP PATCH 和 PUT 方法之間的差異。

我們實施了一個簡單的 Spring REST 控制器,通過 PUT 方法更新資源,並使用 PATCH 進行部分更新。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.