1. 概要

このクイックチュートリアルでは、 HTTP PUT動詞とPATCH動詞の違いと、2つの操作のセマンティクスについて説明します。

Springを使用して、これら2つのタイプの操作をサポートする2つのRESTエンドポイントを実装し、違いとそれらの正しい使用方法をよりよく理解します。

2. いつプットといつパッチを使用するのですか?

単純なステートメントと少し単純なステートメントの両方から始めましょう。

クライアントが既存のリソースを完全に置き換える必要がある場合は、PUTを使用できます。 部分的な更新を行っているときは、HTTPPATCHを使用できます。

たとえば、リソースの1つのフィールドを更新する場合、完全なリソース表現を送信するのは面倒で、多くの不要な帯域幅を使用する可能性があります。 このような場合、PATCHのセマンティクスははるかに理にかなっています。

ここで考慮すべきもう1つの重要な側面はべき等。 PUTはべき等です。 PATCHはべき等である可能性がありますが、必須ではありません。 したがって、実装している操作のセマンティクスに応じて、この特性に基づいてどちらかを選択することもできます。

3. PUTおよびPATCHロジックの実装

複数のフィールドを持つHeavyResourceを更新するためのRESTAPIを実装するとします。

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の作成をスキップして、マップのみを使用することもできます。

@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メソッドの実装を作成する場合、アドレスフィールドの値としてnullを取得した場合のケースの処理方法のコントラクトを指定する必要があります。 HeavyResourceAddressOnly

クライアントが次のリクエストを送信するとします。

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

次に、これをaddressフィールドの値をnullに設定するか、変更なしとして扱うことでそのような要求を無視するように処理できます。

null を処理するための戦略を1つ選び、すべてのPATCHメソッドの実装でそれに固執する必要があります。

6. 結論

このクイック記事では、HTTPPATCHメソッドとPUTメソッドの違いを理解することに焦点を当てました。

単純なSpringRESTコントローラーを実装して、PUTメソッドを介してリソースを更新し、PATCHを使用して部分的に更新しました。

これらすべての例とコードスニペットの実装は、GitHubプロジェクトにあります。 これはMavenプロジェクトなので、そのままインポートして実行するのは簡単です。