REST Complex / Composite / Nested Resources [ditutup]

177

Saya mencoba merangkul kepala saya di sekitar cara terbaik untuk mengatasi konsep dalam API berbasis REST. Sumber daya datar yang tidak mengandung sumber daya lain tidak masalah. Tempat saya mengalami masalah adalah sumber daya yang kompleks.

Misalnya, saya punya sumber daya untuk buku komik. ComicBookmemiliki segala macam properti pada rasanya author, issue number, date, dll

Buku komik juga memiliki daftar 1..nsampul. Penutup ini adalah benda yang kompleks. Mereka berisi banyak informasi tentang sampul: artis, tanggal, dan bahkan gambar dasar 64 yang disandikan dari sampul.

Untuk GETdi ComicBooksaya hanya bisa mengembalikan komik, dan semua selimut termasuk gambar mereka base64'ed. Itu mungkin bukan masalah besar untuk mendapatkan komik tunggal. Tapi misalkan saya sedang membangun aplikasi klien yang ingin membuat daftar semua komik dalam sistem dalam sebuah tabel.
Tabel akan berisi beberapa properti dari ComicBooksumber daya, tetapi kami tentu tidak ingin menampilkan semua sampul dalam tabel. Mengembalikan 1000 buku komik, masing-masing dengan beberapa sampul akan menghasilkan data dalam jumlah sangat besar yang datang dari kawat, data yang tidak perlu bagi pengguna akhir dalam kasus itu.

Naluriku adalah membuat Coversumber daya dan memiliki ComicBooksampul. Jadi sekarang Coveradalah URI. GETpada buku komik berfungsi sekarang, alih-alih Coversumber daya besar yang kami kirim kembali URI untuk setiap sampul dan klien dapat mengambil sumber daya Sampul saat mereka membutuhkannya.

Sekarang saya punya masalah dengan membuat komik baru. Tentunya saya akan ingin membuat setidaknya satu sampul ketika saya membuat Comic, pada kenyataannya itu mungkin aturan bisnis.
Jadi sekarang aku terjebak, aku baik memaksa klien untuk menegakkan aturan bisnis dengan terlebih dahulu mengirimkan Cover, mendapatkan URI untuk menutupi itu, maka POSTing ComicBookdengan yang URI dalam daftar, atau saya POSTdi ComicBookmengambil di sumber daya tampak berbeda dari yang meludah di luar. Sumber daya yang masuk untuk POSTdan GETmerupakan salinan yang dalam, di mana yang keluar GETberisi referensi ke sumber daya yang tergantung.

Sumber Coverdaya mungkin diperlukan dalam hal apa pun karena saya yakin sebagai klien saya ingin membahas arah sampul dalam beberapa kasus. Jadi masalahnya ada dalam bentuk umum terlepas dari ukuran sumber daya dependen. Secara umum bagaimana Anda menangani sumber daya yang kompleks tanpa memaksa klien untuk hanya "tahu" bagaimana sumber daya itu disusun?

jerman
sumber
apakah menggunakan DISCOVERY SERVICE RESTFUL masuk akal?
treecoder
1
Saya mencoba untuk mematuhi HATEAOS yang, menurut saya, bertentangan dengan menggunakan sesuatu seperti itu tetapi saya akan memeriksanya.
jgerman
Pertanyaan berbeda dengan semangat yang sama. Namun kepemilikan berbeda dengan solusi yang Anda usulkan (Yang ada dalam pertanyaan). stackoverflow.com/questions/20951419/...
Wes

Jawaban:

64

@ray, diskusi yang bagus

@ jgerman, jangan lupa hanya karena itu ISTIRAHAT, tidak berarti sumber daya harus diatur dari POST.

Apa yang Anda pilih untuk dimasukkan ke dalam representasi sumber daya apa pun terserah Anda.

Kasus sampul yang dirujuk secara terpisah hanyalah ciptaan sumber daya orang tua (buku komik) yang sumber dayanya (sampul) dapat dirujuk silang. Misalnya, Anda mungkin juga ingin memberikan referensi kepada penulis, penerbit, karakter, atau kategori secara terpisah. Anda mungkin ingin membuat sumber daya ini secara terpisah atau sebelum buku komik yang merujuknya sebagai sumber daya anak. Atau, Anda mungkin ingin membuat sumber daya anak baru setelah membuat sumber daya induk.

Kasus spesifik Anda dari sampul sedikit lebih kompleks karena sampul benar-benar membutuhkan buku komik, dan sebaliknya.

Namun, jika Anda menganggap pesan email sebagai sumber daya, dan alamat dari sebagai sumber daya anak, Anda masih dapat merujuk alamat dari secara terpisah. Misalnya, dapatkan semua dari alamat. Atau, buat pesan baru dengan alamat sebelumnya dari. Jika email REST, Anda dapat dengan mudah melihat bahwa banyak sumber daya rujukan silang dapat tersedia: / pesan-diterima, / konsep-pesan, / dari-alamat, / ke-alamat, / alamat, / subjek, / lampiran, / folder , / tag, / kategori, / label, dkk.

Tutorial ini memberikan contoh yang bagus dari sumber referensi silang. http://www.peej.co.uk/articles/restfully-delicious.html

Ini adalah pola paling umum untuk data yang dihasilkan secara otomatis. Misalnya, Anda tidak memposting URI, ID, atau tanggal pembuatan untuk sumber daya baru, karena ini dihasilkan oleh server. Namun, Anda dapat mengambil URI, ID, atau tanggal pembuatan ketika Anda mendapatkan kembali sumber daya baru.

Contoh dalam kasus data biner Anda. Misalnya, Anda ingin memposting data biner sebagai sumber daya anak. Ketika Anda mendapatkan sumber daya induk, Anda dapat mewakili sumber daya anak tersebut sebagai data biner yang sama, atau sebagai URI yang mewakili data biner.

Bentuk & parameter sudah berbeda dari representasi HTML dari sumber daya. Posting parameter biner / file yang menghasilkan URL bukan peregangan.

Ketika Anda mendapatkan formulir untuk sumber daya baru (/ buku komik / baru), atau mendapatkan formulir untuk mengedit sumber daya (/ buku komik / 0 / edit), Anda meminta representasi spesifik formulir dari sumber daya. Jika Anda mempostingnya ke kumpulan sumber daya dengan tipe konten "application / x-www-form-urlencoded" atau "multipart / form-data", Anda meminta server untuk menyimpan representasi tipe itu. Server dapat merespons dengan representasi HTML yang disimpan, atau apa pun.

Anda mungkin juga ingin memperbolehkan representasi HTML, XML, atau JSON untuk diposkan ke koleksi sumber daya, untuk tujuan API atau sejenisnya.

Dimungkinkan juga untuk mewakili sumber daya dan alur kerja Anda seperti yang Anda gambarkan, dengan memperhitungkan sampul akun yang dipasang setelah buku komik, tetapi membutuhkan buku komik untuk memiliki sampul. Contohnya sebagai berikut.

  • Mengizinkan pembuatan sampul tertunda
  • Mengizinkan pembuatan buku komik dengan sampul yang diperlukan
  • Mengizinkan sampul direferensikan silang
  • Mengizinkan beberapa sampul
  • Buat draft buku komik
  • Buat konsep sampul buku komik
  • Publikasikan buku komik konsep

DAPATKAN / buku komik
=> 200 OK, Dapatkan semua buku komik.

DAPATKAN / buku komik / 0
=> 200 OK, Dapatkan buku komik (id: 0) dengan sampul (/ sampul / 1, / sampul / 2).

DAPATKAN / buku komik / 0 / sampul
=> 200 OK, Dapatkan sampul untuk buku komik (id: 0).

DAPATKAN / mencakup
=> 200 OK, Dapatkan semua penutup.

DAPATKAN / mencakup / 1
=> 200 OK, Dapatkan sampul (id: 1) dengan buku komik (/ buku komik / 0).

DAPATKAN / buku komik / baru
=> 200 OK, Dapatkan formulir untuk membuat buku komik (formulir: POST / draft-buku komik).

POST / draft-komik-buku
judul = foo
penulis = boo
penerbit = goo
diterbitkan = 2011-01-01
=> 302 Ditemukan, Lokasi: / draft-komik-buku / 3, Redirect ke draft buku komik (id: 3) dengan meliputi (biner).

DAPATKAN / draft-komik-buku / 3
=> 200 OK, Dapatkan draft komik buku (id: 3) dengan sampul.

DAPATKAN / draft-komik-buku / 3 / sampul
=> 200 OK, Dapatkan sampul untuk buku komik (/ draft-komik-buku / 3).

DAPATKAN / draft-komik-buku / 3 / sampul / baru
=> 200 OK, Dapatkan formulir untuk membuat sampul untuk draft buku komik (/ draft-komik-buku / 3) (form: POST / draft-komik-buku / 3 / meliputi).

POST / draft-komik-buku / 3 / sampul
cover_type = sampul
depan_data = (biner)
=> 302 Ditemukan, Lokasi: / draft-komik-buku / 3 / sampul, Redirect ke sampul baru untuk buku komik draft (/ draft-komik) -buku / 3 / sampul / 1).

DAPATKAN / draft-komik-buku / 3 / publish
=> 200 OK, Dapatkan formulir untuk menerbitkan draft buku komik (id: 3) (bentuk: POST / buku-komik-terbitan).

POST / publikasi-komik-
judul buku = foo
penulis = boo
penerbit = goo
diterbitkan = 2011-01-01
cover_type = depan
cover_data = (biner)
=> 302 Ditemukan, Lokasi: / komik-buku / 3, Redirect ke buku komik yang diterbitkan (id: 3) dengan sampul.

Alex
sumber
Saya benar-benar pemula dalam hal ini, dan mencoba mempelajarinya dengan tergesa-gesa. Saya menemukan ini sangat membantu. Namun, di blog lain dll. Saya sudah membaca hari ini, penggunaan GET untuk melakukan operasi (terutama operasi yang tidak idempoten) akan disukai. Jadi bukankah seharusnya POST / draft-komik-buku / 3 / publikasikan?
Gary McGill
3
@GaryMcGill Dalam contohnya, / draft-comic-books / 3 / publish hanya mengembalikan formulir HTML (tidak mengubah data apa pun).
Olivier Lalonde
@ Olivier benar. Kata mempublikasikan ada di sana untuk menunjukkan apa yang dilakukan formulir. Namun, karena Anda ingin menjaga kata kerja terbatas pada metode HTTP, Anda harus memposting ke sumber daya untuk buku komik yang diterbitkan. ... Jika ini adalah situs web, Anda mungkin memerlukan URI untuk formulir untuk menerbitkan sesuatu. ... Meskipun, jika tindakan penerbitan hanya satu tombol pada halaman buku komik, formulir satu tombol itu dapat memposting langsung ke / diterbitkan-buku komik URI.
Alex
@Alex, dalam permintaan POST saya malah akan mengembalikan 201 Dibuat, dengan URL sumber daya baru sebagai Lokasi di header respons.
ismriv
2
@Stephane, pengalihan hanya membuat segalanya lebih sederhana untuk pengendali. Bahkan untuk API, lebih mudah memiliki controller buat mengembalikan lokasi untuk konten baru, dan kemudian membiarkan controller acara menangani tampilan konten baru. Meskipun, itu lebih baik / sederhana bagi klien API untuk hanya mendapatkan konten dan tidak repot dengan pengalihan.
Alex
45

Memperlakukan tutup sebagai sumber daya jelas dalam semangat REST, terutama HATEOAS. Jadi ya, GETpermintaan untuk http://example.com/comic-books/1memberi Anda representasi buku 1, dengan properti termasuk seperangkat URI untuk sampul. Sejauh ini baik.

Pertanyaan Anda adalah bagaimana menangani pembuatan buku komik. Jika aturan bisnis Anda adalah bahwa sebuah buku akan memiliki 0 atau lebih sampul, maka Anda tidak memiliki masalah:

POST http://example.com/comic-books

dengan data buku komik tanpa penutup akan membuat buku komik baru dan mengembalikan server yang dihasilkan id (katakanlah kembali menjadi 8), dan sekarang Anda dapat menambahkan sampul seperti:

POST http://example.com/comic-books/8/covers

dengan penutup di badan entitas.

Sekarang Anda memiliki pertanyaan yang bagus, apa yang terjadi jika aturan bisnis Anda mengatakan harus selalu ada setidaknya satu penutup. Berikut adalah beberapa pilihan, yang pertama kali Anda identifikasi dalam pertanyaan Anda:

  1. Dorong pembuatan sampul terlebih dahulu, sekarang pada dasarnya menjadikan sampul sebagai sumber daya yang tidak bergantung, atau Anda menempatkan sampul awal di badan entitas POST yang membuat buku komik. Ini seperti yang Anda katakan berarti bahwa representasi yang Anda POST buat akan berbeda dari representasi yang Anda GET.

  2. Definisikan gagasan tentang penutup utama, atau awal, atau lebih disukai, atau yang ditunjuk lain. Ini kemungkinan merupakan hack pemodelan, dan jika Anda melakukan itu akan seperti mengubah model objek Anda (model konsep atau bisnis Anda) agar sesuai dengan teknologi. Bukan ide yang bagus.

Anda harus mempertimbangkan dua pilihan ini dengan hanya membiarkan komik tanpa penutup.

Manakah dari tiga pilihan yang harus Anda ambil? Tidak tahu terlalu banyak tentang situasi Anda, tetapi jawablah pertanyaan umum 1..N yang tidak tergantung pada sumber daya, saya akan mengatakan:

  • Jika Anda bisa menggunakan 0..N untuk lapisan layanan RESTful Anda, bagus. Mungkin lapisan antara SOA RESTful Anda dapat menangani kendala bisnis lebih lanjut jika setidaknya diperlukan. (Tidak yakin bagaimana kelihatannya tetapi mungkin perlu ditelusuri .... pengguna akhir biasanya tidak melihat SOA.)

  • Jika Anda hanya harus memodelkan kendala 1..N, tanyakan pada diri Anda apakah sampul mungkin hanya sumber daya yang dapat dibagi, dengan kata lain, mereka mungkin ada pada hal-hal lain selain buku komik. Sekarang mereka bukan sumber daya yang tergantung dan Anda dapat membuatnya terlebih dahulu dan memasok URI di POST Anda yang membuat buku komik.

  • Jika Anda membutuhkan 1..N dan penutup tetap tergantung, cukup relakskan insting Anda untuk menjaga agar representasi di POST dan DAPATKAN sama, atau buat sama.

Item terakhir dijelaskan seperti ini:

<comic-book>
  <name>...</name>
  <edition>...</edition>
  <cover-image>...BASE64...</cover-image>
  <cover-image>...BASE64...</cover-image>
  <cover>...URI...</cover>
  <cover>...URI...</cover>
</comic-book>

Ketika Anda POST, Anda mengizinkan uris yang ada jika Anda memilikinya (meminjam dari buku lain) tetapi juga memasukkan satu atau lebih gambar awal. Jika Anda membuat buku dan entitas Anda tidak memiliki gambar sampul awal, kembalikan respons 409 atau serupa. Pada GET Anda dapat mengembalikan URI ..

Jadi pada dasarnya Anda membiarkan representasi POST dan GET menjadi "sama" tetapi Anda hanya memilih untuk tidak "menggunakan" gambar sampul pada GET atau menutupi POST. Harapan itu masuk akal.

Ray Toal
sumber