REST API - Buat Massal atau Perbarui dalam satu permintaan [ditutup]


94

Mari kita asumsikan ada dua sumber Binderdan Docdengan hubungan asosiasi yang berarti Docdan Binderberdiri sendiri. Docmungkin atau mungkin bukan milik Binderdan Bindermungkin kosong.

Jika saya ingin mendesain REST API yang memungkinkan pengguna untuk mengirim koleksi Doc, DALAM PERMINTAAN TUNGGAL , seperti berikut:

{
  "docs": [
    {"doc_number": 1, "binder": 1}, 
    {"doc_number": 5, "binder": 8},
    {"doc_number": 6, "binder": 3}
  ]
}

Dan untuk setiap dokumen di docs,

  • Jika docada, tetapkan keBinder
  • Jika doctidak ada, buat dan tetapkan

Saya benar-benar bingung bagaimana ini harus diterapkan:

  • Metode HTTP apa yang digunakan?
  • Kode respon apa yang harus dikembalikan?
  • Apakah ini memenuhi syarat untuk REST?
  • Bagaimana URI terlihat? /binders/docs?
  • Menangani permintaan massal, bagaimana jika beberapa item menimbulkan kesalahan tetapi yang lain lolos. Kode respon apa yang harus dikembalikan? Haruskah operasi massal menjadi atom?

Jawaban:


59

Saya pikir Anda dapat menggunakan metode POST atau PATCH untuk menangani ini karena mereka biasanya mendesain untuk ini.

  • Menggunakan POSTmetode biasanya digunakan untuk menambahkan elemen saat digunakan pada sumber daya daftar, tetapi Anda juga dapat mendukung beberapa tindakan untuk metode ini. Lihat jawaban ini: Cara Memperbarui Koleksi Sumber Daya REST . Anda juga dapat mendukung format representasi yang berbeda untuk input (jika sesuai dengan array atau elemen tunggal).

    Dalam kasus ini, tidak perlu menentukan format Anda untuk menjelaskan pembaruan.

  • Menggunakan PATCHmetode juga cocok karena permintaan terkait sesuai dengan pembaruan parsial. Menurut RFC5789 ( http://tools.ietf.org/html/rfc5789 ):

    Beberapa aplikasi yang 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 mengubah sumber daya HTTP yang ada.

    Dalam kasus ini, Anda harus menentukan format Anda untuk menjelaskan pembaruan parsial.

Saya pikir dalam kasus ini, POSTdan PATCHsangat mirip karena Anda tidak benar-benar perlu menjelaskan operasi yang harus dilakukan untuk setiap elemen. Saya akan mengatakan bahwa itu tergantung pada format representasi yang akan dikirim.

Kasusnya PUTagak kurang jelas. Faktanya, saat menggunakan metode PUT, Anda harus menyediakan seluruh daftar. Faktanya, representasi yang diberikan dalam permintaan akan menggantikan sumber daya daftar.

Anda dapat memiliki dua opsi terkait jalur sumber daya.

  • Menggunakan jalur sumber daya untuk daftar dokumen

Dalam kasus ini, Anda perlu secara eksplisit memberikan link dokumen dengan pengikat dalam representasi yang Anda berikan dalam permintaan.

Berikut adalah contoh rute untuk ini /docs.

Isi dari pendekatan semacam itu bisa untuk metode POST:

[
    { "doc_number": 1, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 2, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 3, "binder": 5, (other fields in the case of creation) },
    (...)
]
  • Menggunakan jalur sub sumber daya elemen pengikat

Selain itu, Anda juga dapat mempertimbangkan untuk memanfaatkan sub rute untuk menggambarkan hubungan antara dokumen dan pengikat. Petunjuk tentang hubungan antara dokumen dan pengikat sekarang tidak harus ditentukan dalam konten permintaan.

Berikut adalah contoh rute untuk ini /binder/{binderId}/docs. Dalam kasus ini, mengirimkan daftar dokumen dengan metode POSTatau PATCHakan melampirkan dokumen ke pengikat dengan pengenal binderIdsetelah membuat dokumen jika tidak ada.

Isi dari pendekatan semacam itu bisa untuk metode POST:

[
    { "doc_number": 1, (other fields in the case of creation) },
    { "doc_number": 2, (other fields in the case of creation) },
    { "doc_number": 3, (other fields in the case of creation) },
    (...)
]

Mengenai respons, terserah Anda untuk menentukan tingkat respons dan error yang akan dikembalikan. Saya melihat dua level: level status (level global) dan level muatan (level lebih tipis). Itu juga terserah Anda untuk menentukan apakah semua sisipan / pembaruan yang sesuai dengan permintaan Anda harus atom atau tidak.

  • Atom

Dalam hal ini, Anda dapat memanfaatkan status HTTP. Jika semuanya berjalan dengan baik, Anda mendapatkan status 200. Jika tidak, status lain seperti 400jika data yang diberikan tidak benar (misalnya binder id tidak valid) atau yang lainnya.

  • Non atom

Dalam kasus ini, status 200akan dikembalikan dan terserah representasi respons untuk menjelaskan apa yang telah dilakukan dan di mana kesalahan akhirnya terjadi. ElasticSearch memiliki titik akhir di REST API-nya untuk pembaruan massal. Ini dapat memberi Anda beberapa ide pada level ini: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html .

  • Asinkron

Anda juga dapat menerapkan pemrosesan asinkron untuk menangani data yang disediakan. Dalam hal ini, status HTTP yang dikembalikan adalah 202. Klien perlu menarik sumber daya tambahan untuk melihat apa yang terjadi.

Sebelum menyelesaikan, saya juga ingin memperhatikan bahwa spesifikasi OData membahas masalah terkait hubungan antara entitas dengan fitur bernama tautan navigasi . Mungkin Anda bisa melihat ini ;-)

Tautan berikut juga dapat membantu Anda: https://templth.wordpress.com/2014/12/15/designing-a-web-api/ .

Semoga bisa membantu Anda, Thierry


Saya memiliki pertanyaan lanjutan. Saya memilih rute datar tanpa sumber daya sub bersarang. Untuk mendapatkan semua dokumen, saya menelepon GET /docsdan mengambil semua dokumen dalam binder tertentu GET /docs?binder_id=x,. Untuk menghapus subset sumber daya yang akan saya panggil DELETE /docs?binder_id=xatau haruskah saya panggil DELETE /docsdengan {"binder_id": x}dalam isi permintaan? Apakah Anda pernah menggunakan PATCH /docs?binder_id=xuntuk pembaruan batch, atau hanya PATCH /docsdan lulus berpasangan?
Andy Fusniak

35

Anda mungkin perlu menggunakan POST atau PATCH, karena kecil kemungkinannya satu permintaan yang memperbarui dan membuat banyak sumber daya akan menjadi idempoten.

Melakukan PATCH /docsjelas merupakan pilihan yang valid. Anda mungkin menemukan penggunaan format tambalan standar rumit untuk skenario khusus Anda. Tidak yakin tentang ini.

Anda bisa menggunakan 200. Anda juga bisa menggunakan 207 - Multi Status

Ini bisa dilakukan dengan cara yang tenang. Kuncinya, menurut saya, adalah memiliki beberapa sumber daya yang dirancang untuk menerima sekumpulan dokumen untuk diperbarui / dibuat.

Jika Anda menggunakan metode PATCH, menurut saya operasi Anda harus atom. yaitu, saya tidak akan menggunakan kode status 207 dan kemudian melaporkan keberhasilan dan kegagalan dalam badan tanggapan. Jika Anda menggunakan operasi POST, maka pendekatan 207 dapat digunakan. Anda harus merancang badan respons Anda sendiri untuk mengkomunikasikan operasi mana yang berhasil dan mana yang gagal. Saya tidak mengetahui yang standar.


Terima kasih banyak. Dengan This can be done in a RESTful waymaksud Anda Update dan Buat harus dilakukan secara terpisah?
Sam R.

1
@norbertpy Melakukan beberapa jenis operasi tulis pada sumber daya dapat menyebabkan sumber daya lain diperbarui dan dibuat dari satu permintaan. REST tidak memiliki masalah dengan itu. Pilihan frasa saya adalah karena beberapa kerangka kerja menerapkan operasi massal dengan membuat serial permintaan HTTP menjadi dokumen multi-bagian dan kemudian mengirim permintaan HTTP serial sebagai batch. Saya pikir pendekatan itu melanggar batasan REST identifikasi sumber daya.
Darrel Miller

19

PUT ing

PUT /binders/{id}/docs Buat atau perbarui, dan hubungkan satu dokumen ke sebuah binder

misalnya:

PUT /binders/1/docs HTTP/1.1
{
  "docNumber" : 1
}

PATCH ing

PATCH /docs Buat dokumen jika tidak ada dan hubungkan dengan binder

misalnya:

PATCH /docs HTTP/1.1
[
    { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } },
    { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } },
    { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } }
] 

Saya akan menyertakan wawasan tambahan nanti, tetapi sementara itu jika Anda mau, lihat RFC 5789 , RFC 6902, dan William Durand's Please. Jangan Menambal Seperti entri blog Idiot .


2
Terkadang klien membutuhkan operasi massal dan tidak ingin peduli apakah sumber daya ada atau tidak. Seperti yang saya katakan dalam pertanyaan, klien ingin mengirim banyak docsdan mengaitkannya dengan binders. Klien ingin membuat pengikat jika tidak ada dan membuat pengaitan jika ada. Dalam permintaan SATU BULK.
Sam R.

12

Dalam sebuah proyek tempat saya bekerja, kami memecahkan masalah ini dengan menerapkan sesuatu yang kami sebut permintaan 'Batch'. Kami menentukan jalur /batchtempat kami menerima json dalam format berikut:

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 5,
         binder: 8
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      }
   },
]

Responsnya memiliki kode status 207 (Multi-Status) dan terlihat seperti ini:

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
      status: 200
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         error: {
            msg: 'A document with doc_number 5 already exists'
            ...
         }
      },
      status: 409
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      },
      status: 200
   },
]

Anda juga dapat menambahkan dukungan untuk tajuk dalam struktur ini. Kami menerapkan sesuatu yang terbukti berguna yaitu variabel untuk digunakan di antara permintaan dalam satu batch, yang berarti kami dapat menggunakan respons dari satu permintaan sebagai masukan ke permintaan lainnya.

Facebook dan Google memiliki implementasi serupa:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

Ketika Anda ingin membuat atau memperbarui sumber daya dengan panggilan yang sama, saya akan menggunakan POST atau PUT tergantung pada kasusnya. Jika dokumen sudah ada, apakah Anda ingin seluruh dokumen menjadi:

  1. Diganti dengan dokumen yang Anda kirimkan (yaitu properti yang hilang dalam permintaan akan dihapus dan sudah ada ditimpa)?
  2. Digabung dengan dokumen yang Anda kirimkan (yaitu, properti yang hilang dalam permintaan tidak akan dihapus dan properti yang sudah ada akan ditimpa)?

Jika Anda menginginkan perilaku dari alternatif 1 Anda harus menggunakan POST dan jika Anda menginginkan perilaku dari alternatif 2 Anda harus menggunakan PUT.

http://restcookbook.com/HTTP%20Methods/put-vs-post/

Seperti yang sudah disarankan orang, Anda juga bisa menggunakan PATCH, tetapi saya lebih suka menjaga API tetap sederhana dan tidak menggunakan kata kerja tambahan jika tidak diperlukan.


5
Sukai jawaban ini untuk Bukti Konsep serta tautan Google dan Facebook. Tapi tidak setuju dengan bagian akhir tentang POST atau PUT. Dalam 2 kasus jawaban ini disebutkan, yang pertama harus PUT, dan yang kedua harus PATCH.
RayLuo

@RayLuo, dapatkah Anda menjelaskan mengapa kami membutuhkan PATCH selain POST dan PUT?
David Berg

2
Karena untuk itulah PATCH diciptakan. Anda dapat membaca definisi ini dan melihat bagaimana PUT dan PATCH cocok dengan 2 poin Anda.
RayLuo

@DavidBerg, Tampaknya Google lebih menyukai pendekatan lain untuk memproses permintaan batch, yaitu memisahkan header dan isi setiap permintaan sub ke bagian yang sesuai dari permintaan utama, dengan batasan seperti --batch_xxxx. Apakah ada perbedaan penting antara solusi Google dan Facebook? Selain itu, tentang "menggunakan tanggapan dari satu permintaan sebagai masukan ke yang lain", kedengarannya sangat menarik, maukah Anda membagikan lebih banyak detail? atau skenario seperti apa yang harus digunakan?
Yang
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.