Haruskah saya menggunakan PATCH atau PUT di API REST saya?

274

Saya ingin merancang titik akhir istirahat saya dengan metode yang sesuai untuk skenario berikut.

Ada sebuah grup. Setiap grup memiliki status. Grup dapat diaktifkan atau dinonaktifkan oleh admin.

Haruskah saya mendesain titik akhir saya sebagai

PUT /groups/api/v1/groups/{group id}/status/activate

ATAU

PATCH /groups/api/v1/groups/{group id}

with request body like 
{action:activate|deactivate}
java_geek
sumber
1
Keduanya baik-baik saja. Tetapi lihatlah RFC untuk format JSON PATCH ( tools.ietf.org/html/rfc6902 ). PATCH mengharapkan untuk mendapatkan beberapa jenis dokumen diff / patch untuk payload (dan JSON mentah bukan salah satunya).
Jørn Wildt
1
@ JørnWildt no, PUT akan menjadi pilihan yang mengerikan. Apa yang kamu letakkan di sana? PATCH adalah satu-satunya pilihan yang masuk akal. Nah, dalam hal ini Anda bisa menggunakan format PATCH yang disajikan dalam pertanyaan, dan cukup gunakan metode PUT; contoh PUT salah.
thecoshman
3
Tidak ada yang salah dalam mengekspos satu atau lebih properti sebagai sumber daya mandiri yang dapat DAPATKAN klien dan modifikasi dengan PUT. Tetapi, ya, URL tersebut seharusnya / groups / api / v1 / groups / {group id} / status yang dapat Anda PUT "aktif" atau "tidak aktif" atau DAPATKAN untuk membaca keadaan saat ini.
Jørn Wildt
3
Berikut adalah penjelasan yang baik tentang bagaimana PATCH harus benar-benar digunakan: williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot
rishat
4
" activate" tidak memadai konstruksi tenang. Anda mungkin mencoba memperbarui statuske "aktif" atau "tidak aktif". dalam hal ini Anda dapat PATCH ke .../statusdengan string "aktif" atau "tidak aktif" di tubuh. Atau jika Anda mencoba memperbarui boolean di status.active, Anda dapat PATCH ke .../status/activedengan boolean di dalam tubuh
Augie Gardner

Jawaban:

328

The PATCHmetode adalah pilihan yang tepat di sini karena Anda sedang memperbarui sumber daya yang ada - ID grup. PUTseharusnya hanya digunakan jika Anda mengganti sumber daya secara keseluruhan.

Informasi lebih lanjut tentang modifikasi sumber daya parsial tersedia di RFC 5789 . Secara khusus, PUTmetode ini dijelaskan sebagai berikut:

Beberapa aplikasi memperluas Hypertext Transfer Protocol (HTTP) memerlukan fitur untuk melakukan modifikasi sumber daya parsial. Metode HTTP PUT yang ada hanya memungkinkan penggantian dokumen secara lengkap. Proposal ini menambahkan metode HTTP baru, PATCH, untuk memodifikasi sumber HTTP yang ada.

Luke Peterson
sumber
1
Agar adil, Anda bisa PUT string 'aktifkan' atau 'nonaktifkan' ke sumber. Karena (tampaknya) hanya menjadi satu-satunya hal untuk beralih, sepenuhnya menggantikannya bukan masalah besar. Dan itu memungkinkan untuk permintaan yang lebih kecil (tidak signifikan).
thecoshman
35
Penting untuk dicatat bahwa RFC 5789 masih dalam tahap proposal dan belum secara resmi diterima dan saat ini ditandai sebagai 'irrata exist'. 'Praktik terbaik' ini sangat diperdebatkan dan secara teknis PATCH belum menjadi bagian dari standar HTTP.
fishpen0
4
Hanya 2 sen saya beberapa tahun kemudian: Anda dapat menganggap status itu sendiri sebagai sumber daya, dan jika demikian, menggunakan PUT terhadap / status secara teknis akan menggantikan sumber daya status pada titik akhir itu.
Jono Stewart
3
Saya berani membantah dokumen tersebut, meskipun itu "the" RFC. Dokumen menyatakan bahwa Anda harus menggunakan PATCH untuk memodifikasi hanya bagian dari sumber daya, tetapi menghapus hal penting bahwa metode PATCH didefinisikan sebagai metode non-idempoten. Mengapa? Jika metode PUT dibuat dengan pembaruan / penggantian seluruh sumber daya dalam pikiran, lalu mengapa metode PATCH tidak dibuat sebagai metode idempoten seperti PUT, jika tujuannya adalah hanya memperbarui bagian dari sumber daya? Bagi saya, ini lebih merupakan perbedaan dalam idempotensi pembaruan, seperti "a = 5" (PUT) dan "a = a + 5" (PATCH). Keduanya dapat memperbarui seluruh sumber daya.
Mladen B.
179

The R di SISA singkatan sumber daya

(Yang tidak benar, karena mewakili Representasi, tetapi itu adalah trik yang baik untuk mengingat pentingnya Sumberdaya di REST).

Tentang PUT /groups/api/v1/groups/{group id}/status/activate: Anda tidak memperbarui "aktifkan". "Aktifkan" bukanlah sesuatu, itu kata kerja. Kata kerja tidak pernah sumber daya yang baik. Aturan praktis: jika tindakan, kata kerja, ada di URL, itu mungkin tidak tenang .

Apa yang kamu lakukan? Entah Anda "menambahkan", "menghapus" atau "memperbarui" suatu aktivasi pada suatu Grup, atau jika Anda lebih suka: memanipulasi "status" -asumber daya pada suatu Grup. Secara pribadi, saya akan menggunakan "aktivasi" karena mereka kurang ambigu daripada konsep "status": membuat status ambigu, membuat aktivasi tidak.

  • POST /groups/{group id}/activation Membuat (atau meminta pembuatan) aktivasi.
  • PATCH /groups/{group id}/activationMemperbarui beberapa detail dari aktivasi yang ada. Karena grup hanya memiliki satu aktivasi, kami tahu sumber daya aktivasi yang kami maksud.
  • PUT /groups/{group id}/activationMenyisipkan-atau-menggantikan aktivasi yang lama. Karena grup hanya memiliki satu aktivasi, kami tahu sumber daya aktivasi yang kami maksud.
  • DELETE /groups/{group id}/activation Akan membatalkan, atau menghapus aktivasi.

Pola ini berguna ketika "aktivasi" Grup memiliki efek samping, seperti pembayaran yang dilakukan, surat yang dikirim dan sebagainya. Hanya POST dan PATCH yang memiliki efek samping seperti itu. Ketika mis. Penghapusan aktivasi harus, katakanlah, beri tahu pengguna melalui surat, DELETE bukanlah pilihan yang tepat; dalam hal ini Anda mungkin ingin menciptakan sumber daya penonaktifan : POST /groups/{group_id}/deactivation.

Adalah ide yang baik untuk mengikuti pedoman ini, karena kontrak standar ini membuatnya sangat jelas untuk klien Anda, dan semua proksi dan lapisan antara klien dan Anda, tahu kapan aman untuk mencoba lagi, dan kapan tidak. Katakanlah klien berada di suatu tempat dengan wifi yang tidak stabil, dan penggunanya mengklik "nonaktifkan", yang memicu DELETE: Jika gagal, klien hanya dapat mencoba lagi, sampai mendapat 404, 200 atau apa pun yang dapat ditangani. Tetapi jika itu memicu a POST to deactivationia tahu untuk tidak mencoba lagi: POST menyiratkan ini.
Setiap klien sekarang memiliki kontrak, yang, ketika diikuti, akan melindungi terhadap pengiriman 42 email "grup Anda telah dinonaktifkan", hanya karena perpustakaan HTTP-nya terus mencoba kembali panggilan ke backend.

Memperbarui satu atribut: gunakan PATCH

PATCH /groups/{group id}

Jika Anda ingin memperbarui atribut. Misalnya "status" bisa menjadi atribut pada Grup yang dapat diatur. Atribut seperti "status" seringkali merupakan kandidat yang baik untuk dibatasi pada daftar putih nilai. Contoh menggunakan beberapa skema JSON yang tidak ditentukan:

PATCH /groups/{group id} { "attributes": { "status": "active" } }
response: 200 OK

PATCH /groups/{group id} { "attributes": { "status": "deleted" } }
response: 406 Not Acceptable

Mengganti sumber daya, tanpa efek samping menggunakan PUT.

PUT /groups/{group id}

Jika Anda ingin mengganti seluruh Grup. Ini tidak selalu berarti bahwa server benar-benar membuat grup baru dan membuang yang lama, misalnya id mungkin tetap sama. Tetapi untuk klien, inilah yang dapat PUT maksud: klien harus berasumsi bahwa ia mendapatkan item yang sama sekali baru, berdasarkan respons server.

Klien harus, dalam hal PUTpermintaan, selalu mengirim seluruh sumber daya, memiliki semua data yang diperlukan untuk membuat item baru: biasanya data yang sama seperti yang dibutuhkan oleh POST-create.

PUT /groups/{group id} { "attributes": { "status": "active" } }
response: 406 Not Acceptable

PUT /groups/{group id} { "attributes": { "name": .... etc. "status": "active" } }
response: 201 Created or 200 OK, depending on whether we made a new one.

Persyaratan yang sangat penting PUTadalah idempoten: jika Anda memerlukan efek samping saat memperbarui Grup (atau mengubah aktivasi), Anda harus menggunakannya PATCH. Jadi, ketika pembaruan menghasilkan misalnya mengirim surat, jangan gunakan PUT.

berkes
sumber
3
Ini sangat informatif bagi saya. "Pola ini berguna ketika" aktivasi "suatu kelompok memiliki efek samping" - Bagaimana pola ini berguna, khususnya dalam hal kapan tindakan memiliki efek samping, yang bertentangan dengan titik akhir awal OP
Abdul
1
@ Abdul, polanya berguna karena banyak alasan, tetapi efek sampingnya, harus sangat jelas bagi klien, apa efek yang dimiliki suatu tindakan. Ketika, katakanlah, aplikasi iOS memutuskan untuk mengirim seluruh buku alamat sebagai "kontak" itu harus sangat jelas apa efek samping dari membuat, memperbarui, menghapus dll dari Kontak. Untuk menghindari pengiriman semua kontak secara massal, misalnya.
berkelanjutan
1
Dalam RESTfull PUT juga dapat mengubah entitas Identity - Misalnya ID PrimaryKey di mana ia dapat menyebabkan permintaan paralel gagal. (misalnya memperbarui seluruh entitas perlu menghapus beberapa baris dan menambahkan yang baru, maka menciptakan entitas baru) Di mana PATCH tidak boleh bisa melakukan itu, memungkinkan untuk jumlah tak terbatas permintaan PATCH tanpa mempengaruhi "aplikasi" lainnya
Piotr Kula
1
Jawaban yang sangat membantu. Terima kasih! Saya juga akan menambahkan komentar, seperti dalam jawaban Luke, menunjukkan bahwa perbedaan antara PUT / PATCH bukan hanya pembaruan keseluruhan / parsial, itu juga idempotensi yang berbeda. Ini bukan kesalahan, itu adalah keputusan yang disengaja dan saya pikir tidak banyak orang mempertimbangkan hal ini, ketika memutuskan penggunaan metode HTTP.
Mladen B.
1
Layanan @richremer, seperti model, adalah abstraksi internal. Seperti halnya abstraksi yang buruk untuk membutuhkan hubungan 1-1 antara model-model REST-endpoint-dan-ORM atau bahkan database, abstraksi yang buruk untuk mengekspos Layanan. Bagian luar akan, API Anda, harus mengkomunikasikan model domain. Bagaimana Anda menerapkannya secara internal tidak menjadi masalah bagi API. Anda harus bebas untuk berpindah dari ActivationService ke aliran aktivasi berbasis CQRS, tanpa harus mengubah API Anda.
Berkel
12

Saya akan merekomendasikan menggunakan PATCH, karena 'grup' sumber daya Anda memiliki banyak properti tetapi dalam kasus ini, Anda hanya memperbarui bidang aktivasi (modifikasi parsial)

menurut RFC5789 ( https://tools.ietf.org/html/rfc5789 )

Metode HTTP PUT yang ada hanya memungkinkan penggantian dokumen secara lengkap. Proposal ini menambahkan metode HTTP baru, PATCH, untuk memodifikasi sumber HTTP yang ada.

Juga, lebih terinci,

Perbedaan antara permintaan PUT dan PATCH tercermin dalam cara server memproses entitas terlampir untuk memodifikasi sumber daya yang
diidentifikasi oleh Request-URI. Dalam permintaan PUT, entitas terlampir dianggap sebagai versi modifikasi dari sumber daya yang disimpan di
server asal, dan klien meminta agar versi yang disimpan
diganti. Dengan PATCH, bagaimanapun, entitas terlampir berisi serangkaian instruksi yang menggambarkan bagaimana sumber daya yang saat ini berada di
server asal harus dimodifikasi untuk menghasilkan versi baru. Metode PATCH memengaruhi sumber daya yang diidentifikasi oleh Request-URI, dan itu
juga MUNGKIN memiliki efek samping pada sumber daya lainnya; yaitu sumber daya baru
dapat dibuat, atau yang sudah ada dimodifikasi, dengan aplikasi
PATCH.

PATCH tidak aman atau idempoten seperti yang didefinisikan oleh [RFC2616], Bagian 9.1.

Klien harus memilih kapan harus menggunakan PATCH daripada PUT. Sebagai
contoh, jika ukuran dokumen patch yang lebih besar dari ukuran
data sumber daya baru yang akan digunakan dalam PUT, maka mungkin masuk
akal untuk menggunakan PUT bukan PATCH. Perbandingan dengan POST bahkan lebih sulit, karena POST digunakan dalam berbagai cara dan dapat
mencakup operasi PUT dan PATCH seperti jika server memilih. Jika
operasi tidak mengubah sumber daya yang diidentifikasi oleh Request-URI dengan cara yang dapat diprediksi, POST harus dipertimbangkan sebagai ganti PATCH
atau PUT.

Kode respons untuk PATCH adalah

Kode respons 204 digunakan karena respons tidak membawa isi pesan (yang akan dimiliki oleh respons dengan kode 200). Perhatikan bahwa kode sukses lainnya dapat digunakan juga.

lihat juga thttp: //restcookbook.com/HTTP%20Methods/patch/

Peringatan: API yang mengimplementasikan PATCH harus ditambal secara atom. Tidak mungkin sumber daya setengah ditambal saat diminta oleh GET.

Clojurevangelist
sumber
7

Karena Anda ingin mendesain API menggunakan gaya arsitektur REST, Anda perlu memikirkan use case Anda untuk memutuskan konsep mana yang cukup penting untuk diekspos sebagai sumber daya. Jika Anda memutuskan untuk mengekspos status grup sebagai sub-sumber daya, Anda dapat memberikannya URI berikut dan menerapkan dukungan untuk metode GET dan PUT:

/groups/api/groups/{group id}/status

Kelemahan dari pendekatan ini atas PATCH untuk modifikasi adalah bahwa Anda tidak akan dapat membuat perubahan pada lebih dari satu properti grup secara atom dan transaksi. Jika perubahan transaksional penting maka gunakan PATCH.

Jika Anda memutuskan untuk mengekspos status sebagai sub-sumber daya grup, itu harus menjadi tautan dalam representasi grup. Misalnya jika agen mendapat grup 123 dan menerima XML, badan respons dapat berisi:

<group id="123">
  <status>Active</status>
  <link rel="/linkrels/groups/status" uri="/groups/api/groups/123/status"/>
  ...
</group>

Diperlukan hyperlink untuk memenuhi hypermedia sebagai mesin kondisi kondisi aplikasi gaya arsitektur REST.

Andrew Dobrowolski
sumber
0

Saya biasanya lebih suka sesuatu yang sedikit lebih sederhana, seperti activate/ deactivatesub-sumber daya (dihubungkan dengan Linkheader dengan rel=service).

POST /groups/api/v1/groups/{group id}/activate

atau

POST /groups/api/v1/groups/{group id}/deactivate

Untuk konsumen, antarmuka ini sangat sederhana, dan mengikuti prinsip REST tanpa menghalangi Anda dalam membuat konsep "aktivasi" sebagai sumber daya individual.

kaya remer
sumber
0

Salah satu opsi yang memungkinkan untuk menerapkan perilaku tersebut adalah

PUT /groups/api/v1/groups/{group id}/status
{
    "Status":"Activated"
}

Dan jelas, jika seseorang perlu menonaktifkannya, PUTakan Deactivatedberstatus di JSON.

Dalam hal perlunya aktivasi / penonaktifan massal, PATCHdapat melangkah ke dalam game (bukan untuk grup yang tepat, tetapi untuk groupssumber daya:

PATCH /groups/api/v1/groups
{
    { “op”: “replace”, “path”: “/group1/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group7/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group9/status”, “value”: “Deactivated” }
}

Secara umum ini adalah ide yang disarankan oleh @Andrew Dobrowolski, tetapi dengan sedikit perubahan dalam realisasi yang tepat.

Ivan Sokalskiy
sumber