知識庫 / Spring / Spring MVC RSS 訂閱

REST API 中 HTTP PUT 與 HTTP PATCH 的區別

REST,Spring MVC
HongKong
4
03:54 AM · Dec 06 ,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 對象及其所有字段,但我們確實希望能夠僅更新 地址 字段——通過 PATCH 方法。

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

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 中收到 address 字段值為 null 的情況。

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

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

然後我們可以通過將 地址 字段的值設置為 null,或者簡單地忽略此類請求,將其視為無更改來處理它。

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

6. 結論

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

我們通過實現一個簡單的 Spring REST 控制器,來更新資源,利用 PUT 方法進行完整更新,以及利用 PATCH 方法進行部分更新。

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

發佈 評論

Some HTML is okay.