HTTP PUT vs HTTP PATCH dalam REST API

1. Gambaran keseluruhan

Dalam artikel ringkas ini, kita melihat perbezaan antara kata kerja HTTP PUT dan PATCH dan semantik kedua operasi tersebut.

Kami akan menggunakan Spring untuk melaksanakan dua titik akhir REST yang menyokong kedua jenis operasi ini, dan untuk lebih memahami perbezaan dan cara yang tepat untuk menggunakannya.

2. Bilakah Menggunakan Put dan Kapan Patch?

Mari kita mulakan dengan pernyataan yang ringkas dan sederhana.

Apabila pelanggan perlu mengganti Sumber yang ada sepenuhnya, mereka dapat menggunakan PUT. Apabila mereka melakukan pembaharuan separa, mereka dapat menggunakan HTTP PATCH.

Sebagai contoh, ketika memperbarui satu bidang Sumber, mengirimkan representasi Sumber yang lengkap mungkin membebani dan menggunakan banyak lebar jalur yang tidak perlu. Dalam kes seperti itu, semantik PATCH lebih masuk akal.

Aspek penting lain yang perlu dipertimbangkan di sini adalah ketiadaan; PUT tidak berkuasa; PATCH boleh, tetapi tidak diperlukan . Oleh itu - bergantung pada semantik operasi yang kita laksanakan, kita juga dapat memilih satu atau yang lain berdasarkan ciri ini.

3. Melaksanakan Logik PUT dan PATCH

Katakanlah kita ingin menerapkan REST API untuk mengemas kini HeavyResource dengan pelbagai bidang:

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

Pertama, kita perlu membuat titik akhir yang menangani kemas kini penuh sumber 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 standard untuk mengemas kini sumber.

Sekarang, katakan bahawa medan alamat akan sering dikemas kini oleh pelanggan. Dalam kes itu, kami tidak mahu mengirim keseluruhan objek HeavyResource dengan semua bidang , tetapi kami mahukan kemampuan untuk hanya mengemas kini bidang alamat - melalui kaedah PATCH.

Kita boleh membuat HeavyResourceAddressOnly DTO untuk mewakili sebahagian kemas kini bidang alamat:

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

Seterusnya, kita dapat memanfaatkan kaedah PATCH untuk menghantar kemas kini separa:

@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 menghantar bidang yang perlu kita kemas kini sahaja - tanpa overhead untuk menghantar keseluruhan HeavyResource .

Sekiranya kita mempunyai sebilangan besar operasi kemas kini separa ini, kita juga boleh melewatkan pembuatan DTO khusus untuk setiap keluar - 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"); }

Penyelesaian ini akan memberi kita lebih banyak kelonggaran dalam melaksanakan API; namun, kita kehilangan beberapa perkara - seperti pengesahan.

4. Menguji PUT dan PATCH

Akhirnya, mari tulis ujian untuk kedua-dua kaedah HTTP. Pertama, kami ingin menguji kemas kini sumber penuh melalui kaedah 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());

Pelaksanaan kemas kini separa dicapai dengan menggunakan kaedah PATCH:

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

Kami juga boleh menulis ujian 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 Separa Dengan Nilai Nol

Semasa kita menulis implementasi untuk metode PATCH, kita perlu menentukan kontrak bagaimana menangani kes-kes ketika kita batal sebagai nilai untuk bidang alamat di HeavyResourceAddressOnly.

Andaikan pelanggan menghantar permintaan berikut:

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

Maka kita dapat menangani ini sebagai menetapkan nilai bidang alamat untuk batal atau mengabaikan permintaan tersebut dengan menganggapnya sebagai tidak berubah.

Kita harus memilih satu strategi untuk menangani nol dan berpegang teguh pada setiap pelaksanaan metode PATCH.

6. Kesimpulannya

Dalam tutorial ringkas ini, kami memfokuskan diri untuk memahami perbezaan antara kaedah HTTP PATCH dan PUT.

Kami melaksanakan pengawal Spring REST sederhana untuk mengemas kini sumber melalui kaedah PUT dan kemas kini separa menggunakan PATCH.

Pelaksanaan semua contoh dan coretan kod ini terdapat dalam projek GitHub - ini adalah projek Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.