Katakanlah saya memiliki tiga sumber yang terkait seperti:
Grandparent (collection) -> Parent (collection) -> and Child (collection)
Di atas menggambarkan hubungan antara sumber daya ini seperti: Setiap kakek nenek dapat memetakan ke satu atau beberapa orang tua. Setiap orang tua dapat memetakan satu atau beberapa anak. Saya ingin kemampuan untuk mendukung pencarian terhadap sumber daya anak tetapi dengan kriteria filter:
Jika klien saya memberikan saya referensi ke kakek-nenek, saya hanya ingin mencari anak-anak yang merupakan keturunan langsung kakek-nenek itu.
Jika klien saya memberikan saya referensi id kepada orangtua, saya hanya ingin mencari anak-anak yang merupakan keturunan langsung orangtua saya.
Saya telah memikirkan sesuatu seperti ini:
GET /myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text}
dan
GET /myservice/api/v1/parents/{parentID}/children?search={text}
untuk persyaratan di atas, masing-masing.
Tetapi saya juga bisa melakukan sesuatu seperti ini:
GET /myservice/api/v1/children?search={text}&grandparentID={id}&parentID=${id}
Dalam desain ini, saya bisa mengizinkan klien saya mengirimkan saya satu atau yang lain dalam string kueri: baik grandparentID atau parentID, tetapi tidak keduanya.
Pertanyaan saya adalah:
1) Desain API mana yang lebih tenang, dan mengapa? Semantik, mereka berarti dan berperilaku dengan cara yang sama. Sumber daya terakhir di URI adalah "anak-anak", secara efektif menyiratkan bahwa klien beroperasi pada sumber daya anak-anak.
2) Apa pro dan kontra untuk masing-masing dalam hal pemahaman dari perspektif klien, dan rawatan dari perspektif desainer.
3) Apa string query yang benar-benar digunakan, selain "pemfilteran" pada sumber daya Anda? Jika Anda pergi dengan pendekatan pertama, parameter filter tertanam dalam URI itu sendiri sebagai parameter jalur, bukan parameter string kueri.
Terima kasih!
I want to only search against children who are INdirect descendants of that grandparent.
? Menurut struktur Anda, Kakek-nenek tidak memiliki anak langsung.potential design flaw
dan jika Anda memiliki informasi tentang seseorang tetapi tidak ada informasi tentang orang tua mereka, apakah mereka memenuhi syarat sebagaichild
? (mis. Adam dan Hawa) :)Jawaban:
Pertama
Per RFC 3986 §3.4 (Uniform Resource Identifiers § (Komponen Sintaks) | Query
Komponen permintaan adalah untuk pengambilan data non-hierarkis; ada beberapa hal yang lebih hierarkis daripada pohon keluarga! Ergo - terlepas dari apakah Anda menganggapnya "REST-y" atau tidak - agar sesuai dengan format, protokol, dan kerangka kerja dari dan untuk mengembangkan sistem di internet, Anda tidak boleh menggunakan string kueri untuk mengidentifikasi informasi ini.
REST tidak ada hubungannya dengan definisi ini.
Sebelum menjawab pertanyaan spesifik Anda, parameter kueri "pencarian" Anda tidak bernama. Lebih baik memperlakukan segmen kueri Anda sebagai kamus pasangan nilai kunci.
String kueri Anda dapat lebih tepat didefinisikan sebagai
?first_name={firstName}&last_name={lastName}&birth_date={birthDate}
dll.Untuk menjawab pertanyaan spesifik Anda
Saya tidak berpikir ini jelas seperti yang Anda yakini.
Tidak ada antarmuka sumber daya ini yang tenang. The utama prasyarat untuk gaya arsitektur tenang adalah bahwa transisi Aplikasi Negara harus dikomunikasikan dari server sebagai hypermedia. Orang-orang telah bekerja keras dalam struktur URI untuk menjadikan mereka entah bagaimana "URI ISTIRAHAT" tetapi literatur formal mengenai REST sebenarnya tidak banyak bicara tentang ini. Pendapat pribadi saya adalah bahwa banyak dari meta-informasi yang salah tentang REST diterbitkan dengan maksud untuk menghancurkan kebiasaan lama yang buruk. (Membangun sistem yang benar-benar "TENANG" sebenarnya cukup banyak pekerjaan. Industri ini beralih ke "REST" dan mengisi kembali beberapa kekhawatiran ortogonal dengan kualifikasi dan pembatasan yang tidak masuk akal.)
Apa yang dikatakan literatur REST adalah bahwa jika Anda akan menggunakan HTTP sebagai protokol aplikasi Anda, Anda harus mematuhi persyaratan formal spesifikasi protokol dan Anda tidak dapat "membuat http saat Anda pergi dan masih menyatakan bahwa Anda menggunakan http" ; jika Anda akan menggunakan URI untuk mengidentifikasi sumber daya Anda, Anda harus mematuhi persyaratan formal spesifikasi tentang URI / URL.
Pertanyaan Anda ditanggapi langsung oleh RFC3986 §3.4, yang telah saya tautkan di atas. Intinya dalam hal ini adalah bahwa meskipun URI yang sesuai tidak cukup untuk mempertimbangkan API "TENANG", jika Anda ingin sistem Anda benar - benar menjadi "TENANG" dan Anda menggunakan HTTP dan URI, maka Anda tidak dapat mengidentifikasi data hierarkis melalui string kueri karena:
... sesederhana itu.
"Pro" dari dua yang pertama adalah bahwa mereka berada di jalan yang benar . "Kontra" dari yang ketiga adalah tampaknya keliru.
Sejauh menyangkut pengertian Anda dan kemampuan pemeliharaan, hal itu pasti subyektif dan tergantung pada tingkat pemahaman pengembang klien dan potongan desain dari perancang. Spesifikasi URI adalah jawaban pasti tentang bagaimana URI seharusnya diformat. Data hierarkis seharusnya diwakili di jalur dan dengan parameter jalur. Data non-hierarkis seharusnya diwakili dalam kueri. Fragmen ini lebih rumit, karena semantiknya bergantung secara khusus pada jenis media dari representasi yang diminta. Jadi untuk mengatasi komponen "dapat dimengerti" dari pertanyaan Anda, saya akan mencoba menerjemahkan dengan tepat apa yang sebenarnya dikatakan oleh dua URI pertama Anda. Lalu, saya akan mencoba untuk mewakili apa yang Anda katakan ingin Anda capai dengan URI yang valid.
Menerjemahkan URI kata demi kata Anda ke makna semantik mereka
/myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text}
Ini mengatakan untuk orang tua kakek nenek, temukan anak mereka memilikisearch={text}
Apa yang Anda katakan dengan URI Anda hanya masuk akal jika mencari saudara kandung kakek-nenek. Dengan "kakek-nenek, orang tua, anak-anak" Anda menemukan "kakek-nenek" naik satu generasi ke orang tua mereka dan kemudian kembali ke generasi "kakek-nenek" dengan melihat anak-anak orang tua./myservice/api/v1/parents/{parentID}/children?search={text}
Ini mengatakan bahwa untuk induk yang diidentifikasi oleh {parentID}, cari anak mereka memiliki?search={text}
ini. Ini lebih dekat untuk mengoreksi apa yang Anda inginkan, dan mewakili hubungan orangtua-> anak yang mungkin dapat digunakan untuk memodelkan seluruh API Anda. Untuk memodelkannya dengan cara ini, beban ditempatkan pada klien untuk mengenali bahwa jika mereka memiliki "kakek-nenek", bahwa ada lapisan tipuan antara ID yang mereka miliki dan bagian dari grafik keluarga yang ingin mereka lihat. Untuk menemukan "anak" oleh "kakek-nenek", Anda dapat menghubungi/parents/{parentID}/children
layanan Anda dan kemudian memeriksa anak yang dikembalikan, cari anak-anak mereka untuk pengidentifikasi orang Anda.Implementasi persyaratan Anda sebagai URI. Jika Anda ingin memodelkan pengenal sumber daya yang lebih dapat dikembangkan yang dapat berjalan di atas pohon, saya dapat memikirkan beberapa cara untuk mencapai itu.
1) Yang pertama, saya sudah singgung. Mewakili grafik "Orang" sebagai struktur komposit. Setiap orang memiliki referensi ke generasi di atasnya melalui jalur Orang Tua dan ke generasi di bawahnya melalui jalur Anak-anaknya.
/Persons/Joe/Parents/Mother/Parents
akan menjadi cara untuk mengambil kakek dari pihak ibu Joe./Persons/Joe/Parents/Parents
akan menjadi cara untuk mengambil semua kakek-nenek Joe./Persons/Joe/Parents/Parents?id={Joe.GrandparentID}
akan mengambil kakek Joe memiliki pengenal yang Anda miliki.dan ini semua masuk akal (perhatikan bahwa mungkin ada penalti kinerja di sini tergantung pada tugas dengan memaksa dfs di server karena kurangnya identifikasi cabang dalam pola "Orang Tua / Orang Tua / Orangtua".) Anda juga mendapat manfaat dari memiliki kemampuan untuk mendukung sejumlah generasi yang sewenang-wenang. Jika, karena alasan tertentu, Anda ingin melihat 8 generasi, Anda dapat mewakili ini sebagai
/Persons/Joe/Parents/Parents/Parents/Parents/Parents/Parents/Parents/Parents?id={Joe.NotableAncestor}
tetapi ini mengarah ke opsi dominan kedua untuk mewakili data ini: melalui parameter jalur.
2) Gunakan parameter jalur untuk "kueri hierarki" Anda bisa mengembangkan struktur berikut untuk membantu meringankan beban konsumen dan masih memiliki API yang masuk akal.
Untuk melihat kembali ke 147 generasi, mewakili pengidentifikasi sumber daya ini dengan parameter jalur memungkinkan Anda melakukannya
/Persons/Joe/Parents;generations=147?id={Joe.NotableAncestor}
Untuk menemukan Joe dari kakek-nenek buyutnya, Anda dapat melihat ke bawah grafik sejumlah generasi yang dikenal untuk Joe's Id.
/Persons/JoesGreatGrandparent/Children;generations=3?id={Joe.Id}
Hal utama yang perlu diperhatikan dengan pendekatan ini adalah bahwa tanpa informasi lebih lanjut dalam pengidentifikasi dan permintaan, Anda harus berharap bahwa URI pertama adalah mengambil Orang 147 generasi dari Joe dengan pengidentifikasi Joe.NotableAncestor. Anda harus mengharapkan yang kedua untuk mengambil Joe. Asumsikan bahwa apa yang sebenarnya Anda inginkan adalah klien panggilan Anda untuk dapat mengambil seluruh rangkaian node dan hubungan mereka antara Person root dan konteks akhir URI Anda. Anda dapat melakukannya dengan URI yang sama (dengan beberapa hiasan tambahan) dan mengatur Penerimaan
text/vnd.graphviz
atas permintaan Anda, yang merupakan jenis media terdaftar IANA untuk.dot
representasi grafik. Dengan itu, ubah URI ke/Persons/Joe/Parents;generations=147?id={Joe.NotableAncestor.Id}#directed
dengan HTTP Request Header
Accept: text/vnd.graphviz
dan Anda dapat meminta klien berkomunikasi dengan cukup jelas bahwa mereka menginginkan grafik arahan hierarki generasi antara Joe dan 147 generasi sebelumnya di mana generasi leluhur ke-147 itu mengandung seseorang yang diidentifikasi sebagai "Nenek Moyang Terkemuka" Joe.Saya tidak yakin apakah teks / vnd.graphviz memiliki semantik yang telah ditentukan sebelumnya untuk fragmennya; Saya tidak dapat menemukannya dalam pencarian instruksi. Jika jenis media itu memang memiliki informasi fragmen yang telah ditentukan sebelumnya, maka semantiknya harus diikuti untuk membuat URI yang sesuai. Tetapi, jika semantik tersebut tidak ditentukan sebelumnya, spesifikasi URI menyatakan bahwa semantik pengidentifikasi fragmen tidak dibatasi dan sebagai gantinya didefinisikan oleh server, menjadikan penggunaan ini valid.
Saya percaya saya sudah benar-benar mengalahkan ini sampai mati, tetapi string kueri bukan untuk sumber daya "penyaringan". Mereka untuk mengidentifikasi sumber daya Anda dari data non-hierarkis. Jika Anda telah menelusuri hierarki Anda dengan jalan Anda dengan pergi
/person/{id}/children/
dan Anda ingin mengidentifikasi anak tertentu atau sekelompok anak tertentu , Anda akan menggunakan beberapa atribut yang berlaku untuk set yang Anda identifikasi dan memasukkannya ke dalam kueri.sumber
Di sinilah Anda salah:
Dalam sistem REST, klien tidak boleh diganggu dengan ID. Satu-satunya pengidentifikasi sumber daya yang harus diketahui klien adalah URI. Ini adalah prinsip "antarmuka seragam".
Pikirkan tentang bagaimana klien akan berinteraksi dengan sistem Anda. Katakanlah pengguna mulai menelusuri daftar kakek-nenek, ia memilih salah satu anak kakek-nenek, yang membawanya ke sana
/grandparent/123
. Jika klien harus dapat mencari anak-anak/grandparent/123
, maka menurut "HATEOAS", apa pun yang dikembalikan ketika Anda melakukan kueri/grandparent/123
harus mengembalikan URL ke antarmuka pencarian. URL ini harus sudah memiliki data apa pun yang diperlukan untuk disaring oleh kakek saat ini tertanam di dalamnya.Apakah link tersebut tampak seperti
/grandparent/123?search={term}
atau/parent?grandparent=123&search={term}
atau/parent?grandparentTerm=someterm&someothergplocator=blah&search={term}
yang tidak penting menurut REST. Perhatikan bagaimana semua URL tersebut memiliki jumlah parameter yang sama{term}
, yaitu , meskipun mereka menggunakan kriteria yang berbeda. Anda dapat beralih di antara URL-URL itu atau Anda dapat mencampurkannya tergantung pada kakek-nenek spesifik dan klien tidak akan putus, karena hubungan logis antara sumber daya adalah sama meskipun implementasi yang mendasarinya mungkin berbeda secara signifikan.Jika Anda malah membuat layanan sedemikian rupa sehingga diperlukan
/grandparent/{grandparentID}?search={term}
ketika Anda pergi ke satu arah tetapi/children?parent={parentID}&search={term}
a} ketika Anda pergi ke arah lain, itu terlalu banyak penggabungan karena klien harus tahu untuk menginterpolasi hal-hal yang berbeda pada hubungan yang berbeda yang secara konsep mirip.Apakah Anda benar-benar setuju
/grandparent/123?search={term}
atau/parent?grandparent=123&search={term}
hanya dengan selera dan implementasi mana yang lebih mudah bagi Anda saat ini. Yang penting adalah untuk tidak mengharuskan klien untuk dimodifikasi jika Anda mengubah strategi URL Anda atau jika Anda menggunakan strategi yang berbeda pada hubungan orang tua-anak yang berbeda.sumber
Saya tidak yakin mengapa orang berpikir menempatkan nilai ID dalam URL berarti entah bagaimana REST API, REST adalah tentang penanganan kata kerja, melewati sumber daya.
Jadi jika Anda ingin PUT pengguna baru, Anda harus mengirim data yang adil dan permintaan POST http sangat ideal, jadi meskipun Anda mungkin mengirim kunci (mis. Id pengguna), Anda akan mengirim data pengguna (mis. nama, alamat) sebagai data POST.
Sekarang adalah idiom umum untuk menempatkan pengidentifikasi sumber daya dalam URI, tetapi ini lebih konvensi daripada bentuk kanonik "ini bukan REST jika tidak di dalam URI". Ingatlah bahwa tesis awal REST tidak benar-benar menyebutkan http sama sekali, ini merupakan ungkapan untuk menangani data di klien-server, bukan sesuatu yang merupakan ekstensi ke http (meskipun, jelas, http adalah bentuk utama kami menerapkan REST) .
Misalnya, Fielding menggunakan permintaan makalah akademis sebagai contoh. Anda ingin mengambil sumber "Kertas Dr John tentang minum bir", tetapi Anda mungkin juga menginginkan versi awal, atau versi terbaru, sehingga pengidentifikasi sumber daya mungkin bukan sesuatu yang mudah dirujuk sebagai satu ID yang dapat ditempatkan di URI. REST memungkinkan untuk ini dan maksud di baliknya adalah:
Jadi tidak ada yang menghentikan Anda dari menggunakan URI statis untuk mengambil orang tua Anda, memasukkan istilah pencarian dalam string kueri untuk mengidentifikasi pengguna yang Anda cari. Dalam hal ini, 'sumber daya' yang Anda identifikasi adalah himpunan kakek-nenek (dan oleh karena itu URI berisi 'kakek-nenek' sebagai bagian dari URI. REST mencakup konsep 'data kontrol' yang dirancang untuk menentukan representasi mana dari Anda sumber daya harus diambil - contoh yang diberikan dalam ini adalah kontrol cache, tetapi juga kontrol versi - jadi permintaan saya untuk makalah Dr John yang sangat baik dapat disempurnakan dengan melewatkan versi sebagai data kontrol, bukan bagian dari URI.
Saya pikir contoh antarmuka REST yang biasanya tidak disebutkan adalah SMTP . Saat membuat pesan email, Anda mengirim kata kerja (DARI, KE, dll) dengan data sumber daya untuk setiap bagian dari pesan email. Ini tenang meskipun tidak menggunakan kata kerja http, ia menggunakan set sendiri.
Jadi ... sementara Anda perlu memiliki beberapa identifikasi sumber daya di URI Anda, itu tidak harus menjadi referensi id Anda. Ini dapat dengan senang hati dikirim sebagai data kontrol, dalam string kueri atau bahkan dalam data POST. Apa yang benar-benar Anda identifikasi di REST API Anda adalah bahwa Anda mengejar anak, yang sudah Anda miliki di URI Anda.
Jadi menurut saya, membaca definisi REST, Anda meminta sumber daya anak, menyerahkan data kontrol (dalam bentuk querystring) untuk menentukan mana yang ingin Anda kembalikan. Akibatnya, Anda tidak dapat membuat permintaan URI untuk kakek atau nenek. Anda ingin anak - anak kembali sehingga istilah orang tua / kakek-nenek atau id tidak boleh dalam URI seperti itu. Seharusnya anak-anak.
sumber
Banyak orang sudah membicarakan tentang arti REST, dll. Tapi sepertinya tidak ada yang membahas masalah sebenarnya: desain Anda.
Mengapa kakek dan nenek berbeda dari seorang ayah? mereka berdua memiliki anak yang mungkin dapat memiliki anak yang ...
Akhirnya, mereka semua 'manusia'. Anda mungkin memilikinya dalam kode Anda juga. Jadi gunakan itu:
Akan mengembalikan beberapa info bermanfaat tentang manusia. (Nama, dan lainnya)
jelas akan mengembalikan array anak-anak. Jika tidak ada anak, susunan kosong mungkin merupakan cara yang harus dilakukan.
Untuk membuatnya mudah, misalnya Anda dapat menambahkan bendera 'hasChildren'. atau 'hasGrandChildren'.
sumber
Berikut ini lebih TENANG karena setiap kakek-nenek mendapatkan URL sendiri. Dengan cara ini sumber daya diidentifikasi dengan cara yang unik.
Pencarian parameter kueri adalah cara yang baik untuk melakukan pencarian dari konteks sumber daya itu.
Ketika keluarga menjadi sangat besar, Anda dapat menggunakan mulai / batas sebagai opsi permintaan misalnya:
Sebagai pengembang, ada baiknya memiliki sumber daya yang berbeda dengan URL / URI yang unik. Saya pikir Anda harus menggunakan parameter permintaan hanya ketika mereka juga bisa ditinggalkan.
Mungkin ini adalah yang baik baca http://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling dan sebaliknya tesis PhD asli Roy T Fielding https://www.ics.uci.edu /~fielding/pubs/dissertation/fielding_dissertation.pdf yang menjelaskan konsep dengan sangat baik dan lengkap.
sumber
Saya akan pergi dengan
Dalam hal ini setiap awalan adalah sumber yang valid:
/myservice/api/v1/people
: semua orang/myservice/api/v1/people/{personID}
: satu orang dengan informasi lengkap (termasuk leluhur, saudara kandung, keturunan)/myservice/api/v1/people/{personID}/descendants
: keturunan satu orang/myservice/api/v1/people/{personID}/descendants/2
: cucu satu orangMungkin bukan jawaban terbaik, tapi setidaknya masuk akal bagi saya.
sumber
banyak sebelum saya telah menunjukkan bahwa format URL tidak penting dalam konteks layanan RESTful ... dua sen saya akan menjadi ... aspek penting dari REST seperti yang dianjurkan dalam penulisan asli adalah konsep 'Sumber Daya' .. .ketika Anda mendesain URL Anda, satu aspek yang mungkin perlu Anda ingat adalah bahwa 'Sumber Daya' bukan satu baris dalam satu tabel (meskipun bisa jadi) ..atau itu adalah representasi..itu juga konsisten .. .dan dapat digunakan untuk membuat perubahan pada kondisi representasi tersebut (dan secara efektif menjadi media penyimpanan), dan sumber daya yang diberikan mungkin hanya bermakna dalam konteks bisnis Anda .. misalnya;
Dalam contoh Anda / layanan saya / api / v1 / kakek-nenek / {grandparentID} / orang tua / anak-anak? Search = {text}
Bisa menjadi sumber yang lebih bermakna jika Anda mempersingkat ini / myservice / api / v1 / siblingsOfGrandParents? Search = ..
dalam hal ini Anda dapat mendeklarasikan 'siblingsOfGrandParents' sebagai sumber daya..ini menyederhanakan URL
Seperti yang ditunjukkan oleh orang lain, ada gagasan sesat yang tersebar luas yang perlu Anda masukkan dalam setiap jenis hubungan hierarkis antara objek domain dalam bentuk yang lebih eksplisit dalam URL .. tidak ada aturan yang keras dan cepat untuk memiliki pemetaan 1-to1 antara objek domain dan sumber daya dan mewakili semua hubungan mereka ... pertanyaannya seharusnya saya percaya ... apakah ada kebutuhan untuk mengekspos hubungan tersebut melalui URL ... khususnya segmen jalan ... mungkin tidak.
sumber