HTTP PUT vs HTTP PATCH di REST API

1. Ikhtisar

Dalam artikel singkat ini, kami melihat perbedaan antara kata kerja HTTP PUT dan PATCH dan pada semantik dari kedua operasi tersebut.

Kami akan menggunakan Spring untuk mengimplementasikan dua titik akhir REST yang mendukung dua jenis operasi ini, dan untuk lebih memahami perbedaan dan cara yang tepat untuk menggunakannya.

2. Kapan Menggunakan Put dan Kapan Menambal?

Mari kita mulai dengan pernyataan yang sederhana dan sedikit sederhana.

Ketika klien perlu mengganti Sumber Daya yang ada seluruhnya, mereka dapat menggunakan PUT. Saat mereka melakukan pembaruan parsial, mereka dapat menggunakan HTTP PATCH.

Misalnya, saat memperbarui satu bidang Sumber Daya, mengirim representasi Sumber Daya lengkap mungkin merepotkan dan menggunakan banyak bandwidth yang tidak perlu. Dalam kasus seperti itu, semantik PATCH jauh lebih masuk akal.

Aspek penting lainnya untuk dipertimbangkan di sini adalah idempotensi; PUT idempoten; PATCH bisa, tetapi tidak diharuskan . Dan, jadi - bergantung pada semantik operasi yang kami terapkan, kami juga dapat memilih satu atau lainnya berdasarkan karakteristik ini.

3. Menerapkan PUT dan PATCH Logic

Katakanlah kita ingin menerapkan REST API untuk memperbarui HeavyResource dengan banyak bidang:

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

Pertama, kita perlu membuat titik akhir yang menangani pembaruan penuh sumber daya menggunakan PUT:

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

Ini adalah titik akhir standar untuk memperbarui sumber daya.

Sekarang, katakanlah bidang alamat akan sering diperbarui oleh klien. Dalam hal ini, kami tidak ingin mengirim seluruh objek HeavyResource dengan semua bidang , tetapi kami ingin kemampuan untuk hanya memperbarui bidang alamat - melalui metode PATCH.

Kita dapat membuat DTO HeavyResourceAddressOnly untuk mewakili pembaruan sebagian bidang alamat:

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

Selanjutnya, kita dapat memanfaatkan metode PATCH untuk mengirim pembaruan parsial:

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

Dengan DTO yang lebih terperinci ini, kita dapat mengirim bidang yang hanya perlu kita perbarui - tanpa biaya pengiriman seluruh HeavyResource .

Jika kita memiliki sejumlah besar operasi pembaruan parsial ini, kita juga dapat melewatkan pembuatan DTO khusus untuk setiap keluaran - dan hanya menggunakan peta:

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

Solusi ini akan memberi kami lebih banyak fleksibilitas dalam mengimplementasikan API; namun, kami juga kehilangan beberapa hal - seperti validasi.

4. Pengujian PUT dan PATCH

Terakhir, mari tulis pengujian untuk kedua metode HTTP. Pertama, kami ingin menguji pembaruan sumber daya lengkap melalui metode 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());

Eksekusi pembaruan parsial dicapai dengan menggunakan metode PATCH:

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

Kami juga dapat menulis tes untuk pendekatan yang lebih umum:

HashMap 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. Menangani Permintaan Parsial Dengan Nilai Null

Saat kita menulis implementasi untuk metode PATCH, kita perlu menentukan kontrak bagaimana menangani kasus ketika kita mendapatkan null sebagai nilai untuk bidang alamat di HeavyResourceAddressOnly.

Misalkan klien mengirimkan permintaan berikut:

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

Kemudian kita dapat menangani ini sebagai menyetel nilai bidang alamat menjadi null atau hanya mengabaikan permintaan tersebut dengan memperlakukannya sebagai tidak ada perubahan.

Kita harus memilih satu strategi untuk menangani null dan mematuhinya dalam setiap implementasi metode PATCH.

6. Kesimpulan

Dalam tutorial singkat ini, kami fokus untuk memahami perbedaan antara metode HTTP PATCH dan PUT.

Kami menerapkan pengontrol Spring REST sederhana untuk memperbarui Sumber Daya melalui metode PUT dan pembaruan parsial menggunakan PATCH.

Penerapan semua contoh dan cuplikan kode ini dapat ditemukan di proyek GitHub - ini adalah proyek Maven, jadi semestinya mudah untuk mengimpor dan menjalankannya apa adanya.