Beberapa konsep yang berkaitan dengan konflik REST di kepala saya ketika saya mencoba menerapkannya.
Saya memiliki sistem API back-end REST-ful yang memegang logika bisnis, dan aplikasi web yang menyediakan UI. Dari berbagai sumber tentang REST (khususnya, REST dalam Praktek: Hypermedia dan Arsitektur Sistem ) saya tahu bahwa saya tidak boleh mengekspos pengidentifikasi mentah entitas saya, melainkan mengembalikan hyperlink dengan rel="self"
.
Perhatikan contohnya. Api REST memiliki sumber daya yang mengembalikan seseorang:
<Person>
<Links>
<Link rel="self" href="http://my.rest.api/api/person/1234"/>
</Links>
<Pets>
<Link rel="pet" href="http://my.rest.api/api/pet/678"/>
</Pets>
</Person>
Masalah muncul dengan aplikasi web. Mari kita asumsikan itu mengembalikan halaman yang berisi hyperlink ke browser:
<body class="person">
<p>
<a href="http://my.web.app/pet/???????" />
</p>
</body>
Apa yang harus saya masukkan ke href
atribut? Bagaimana cara saya menjaga URL entitas API di aplikasi web agar bisa mendapatkan entitas ketika pengguna membuka halaman target?
Persyaratannya tampaknya saling bertentangan:
- Hyperlink
href
harus mengarah ke aplikasi web karena itu adalah sistem hosting UI - The
href
harus memiliki beberapa id entitas karena aplikasi web harus mampu mengatasi entitas ketika halaman target terbuka - Aplikasi web seharusnya tidak mem-parsing / membuat URL REST karena itu bukan REST-ful, buku tersebut mengatakan
URI harus buram bagi konsumen. Hanya penerbit URI yang tahu bagaimana menafsirkannya dan memetakannya ke sumber daya.
Jadi, saya tidak bisa hanya mengambil 1234
dari URL respons API karena sebagai klien yang tenang saya harus memperlakukannya seolah-olah itu seperti sesuatu http://my.rest.api/api/AGRIDd~ryPQZ^$RjEL0j
. Di sisi lain, saya harus memberikan beberapa URL yang mengarah ke aplikasi web saya dan cukup bagi aplikasi untuk mengembalikan URL asli API dan menggunakan URL itu untuk mengakses sumber daya API.
Cara paling sederhana mungkin hanya menggunakan URL sumber daya API sebagai pengidentifikasi string mereka. Tetapi url halaman web seperti http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234
itu jelek.
Itu semua tampaknya cukup mudah untuk aplikasi desktop atau aplikasi javascript satu halaman. Karena mereka hidup terus menerus, mereka hanya dapat menyimpan URL dalam memori bersama dengan objek layanan untuk masa aplikasi dan menggunakannya saat diperlukan.
Dengan aplikasi web saya bisa membayangkan beberapa pendekatan, tetapi semuanya tampak aneh:
- Ganti host di URL API dan simpan hasilnya saja. Kelemahan besar adalah bahwa itu memerlukan aplikasi web untuk menangani URL apa pun yang dihasilkan API, yang berarti kopling mengerikan. Selain itu, ini tidak tenang lagi, karena aplikasi web saya mulai menafsirkan URL.
- Paparkan id mentah di REST API bersama dengan tautan, gunakan untuk membuat URL Aplikasi Web, dan kemudian gunakan id di server aplikasi web untuk menemukan sumber daya yang diperlukan dalam API. Ini lebih baik, tetapi akan mempengaruhi kinerja server aplikasi web karena aplikasi web harus melalui navigasi layanan REST yang mengeluarkan rantai permintaan get-by-id dari beberapa formulir untuk menangani permintaan dari browser. Untuk sumber daya yang agak bersarang ini mungkin mahal.
- Simpan semua
self
URL yang dikembalikan oleh api dalam pemetaan persisten (DB?) Di server aplikasi web. Buat beberapa id untuk mereka, gunakan id untuk membangun URL halaman aplikasi web dan untuk mendapatkan URL sumber daya layanan REST. Yaitu saya menyimpanhttp://my.rest.api/pet/678
URL di suatu tempat dengan kunci baru, katakanlah3
, dan hasilkan URL halaman web sebagaihttp://my.web.app/pet/3
. Ini terlihat seperti implementasi HTTP Cache. Saya tidak tahu mengapa, tapi itu terasa aneh bagi saya.
Atau Apakah itu semua berarti bahwa RESTful API tidak dapat berfungsi sebagai backend untuk aplikasi web?
sumber
Jawaban:
Diedit untuk menjawab pembaruan pertanyaan, jawaban sebelumnya dihapus
Melihat perubahan Anda pada pertanyaan Anda, saya pikir saya mengerti masalah yang Anda hadapi lebih sedikit. Karena tidak ada bidang yang merupakan pengidentifikasi pada sumber daya Anda (hanya sebuah tautan), Anda tidak memiliki cara untuk merujuk sumber daya spesifik itu dalam GUI Anda (yaitu tautan ke halaman yang menggambarkan hewan peliharaan tertentu).
Hal pertama yang harus ditentukan adalah apakah hewan peliharaan masuk akal tanpa pemilik. Jika kita dapat memiliki hewan peliharaan tanpa pemilik, maka saya akan mengatakan kita perlu semacam properti unik pada hewan peliharaan yang dapat kita gunakan untuk merujuknya. Saya tidak percaya ini akan melanggar jika tidak mengekspos ID secara langsung karena ID sumber daya yang sebenarnya masih tersimpan di tautan yang tidak diuraikan oleh klien REST. Dengan mengingat hal itu, sumber daya hewan peliharaan kami mungkin terlihat seperti:
Kami sekarang dapat memperbarui nama hewan peliharaan itu dari Spot ke Fido tanpa harus mengacaukan ID sumber daya yang sebenarnya di seluruh aplikasi. Kita juga bisa merujuk ke hewan peliharaan itu di GUI kita dengan sesuatu seperti:
Jika hewan peliharaan tidak masuk akal tanpa pemilik (atau hewan peliharaan tidak diperbolehkan dalam sistem tanpa pemilik) maka kita dapat menggunakan pemilik sebagai bagian dari "identitas" hewan peliharaan dalam sistem:
Satu catatan kecil, jika Hewan Peliharaan dan Manusia bisa terpisah satu sama lain, saya tidak akan membuat titik masuk untuk API sumber daya "Manusia". Alih-alih, saya akan membuat sumber daya yang lebih umum yang akan berisi tautan ke People and Pets. Itu bisa mengembalikan sumber daya yang terlihat seperti:
Jadi dengan hanya mengetahui titik masuk pertama ke dalam API dan tidak memproses URL apa pun untuk mengetahui pengidentifikasi sistem, kami dapat melakukan sesuatu seperti ini:
Pengguna masuk ke dalam aplikasi. Klien REST mengakses seluruh daftar sumber daya orang yang tersedia yang mungkin terlihat seperti:
GUI akan mengulangi setiap sumber daya dan mencetak item daftar untuk setiap orang menggunakan UniqueName sebagai "id":
Saat melakukan ini, ia juga dapat memproses setiap tautan yang ditemukannya dengan rel "pet" dan mendapatkan sumber daya pet seperti:
Dengan ini, ia dapat mencetak tautan seperti:
atau
Jika kita menggunakan tautan pertama dan menganggap bahwa sumber entri kami memiliki tautan dengan relasi "hewan peliharaan", aliran kontrol akan seperti ini di GUI:
Menggunakan tautan kedua akan menjadi rangkaian acara yang serupa dengan pengecualian bahwa Orang adalah titik masuk ke API dan kami pertama-tama akan mendapatkan daftar semua orang dalam sistem, menemukan yang cocok, lalu menemukan semua hewan peliharaan yang termasuk kepada orang itu (menggunakan tag rel lagi) dan temukan yang bernama Spot sehingga kami dapat menampilkan informasi spesifik yang terkait dengannya.
sumber
rel
s untuk memilih tautan, tetapi tidak boleh mengasumsikan pengetahuan tentang struktur URL. REST menegaskan bahwa API bebas untuk mengubah URL sesuka hati asalkanrel
tetap sama. Parsing URL menggeser kami lebih dekat ke SOAP daripada ke REST.Saya menantang apakah perlu membedakan antara REST API dan aplikasi web. Anda "aplikasi web" hanya harus alternatif (HTML) representasi dari sumber yang sama - yang mengatakan, saya tidak mengerti bagaimana atau mengapa Anda harapkan untuk akses
http://my.rest.api/...
danhttp://my.web.app/...
dan bahwa mereka secara bersamaan yang sama dan berbeda."Klien" Anda adalah browser dalam hal ini dan mengerti HTML dan JavaScript. Itu adalah aplikasi web menurut saya. Sekarang Anda mungkin tidak setuju dan berpikir bahwa Anda mengakses aplikasi web tersebut menggunakan foo.com dan mengekspos yang lain melalui api.foo.com - tetapi Anda harus bertanya, bagaimana foo.com memberi saya representasi sumber daya? "Back-end" dari foo.com sangat mampu memahami bagaimana menemukan sumber daya dari api.foo.com. Aplikasi web Anda hanya menjadi proxy - tidak berbeda dengan jika Anda berbicara dengan API lain (dari orang lain) secara bersamaan.
Jadi pertanyaan Anda dapat digeneralisasi menjadi, "Bagaimana saya bisa menggambarkan sumber daya menggunakan URI saya sendiri yang ada di sistem lain?" yang sepele ketika Anda menganggap bahwa itu bukan klien (HTML / JavaScript) yang harus mengerti bagaimana melakukan ini, tetapi server. Jika Anda setuju dengan tantangan pertama saya, maka Anda bisa menganggap aplikasi web Anda sebagai REST API terpisah yang mewakili atau mendelegasikan ke REST API lain.
Jadi, ketika klien Anda mengaksesnya,
my.web.app/pets/1
ia tahu untuk menghadirkan antarmuka hewan peliharaan karena baik itu yang dikembalikan oleh templat sisi server, atau jika itu permintaan asinkron untuk beberapa representasi lain (misalnya JSON atau XML), header tipe konten memberi tahu begitu .Server yang menyediakan ini adalah yang bertanggung jawab untuk memahami apa yang dimaksud dengan hewan peliharaan dan bagaimana menemukan hewan peliharaan di sistem jarak jauh. Cara Anda melakukan ini terserah Anda - Anda dapat mengambil ID dan membuat URI lain, yang menurut Anda tidak pantas, atau Anda dapat memiliki basis data sendiri yang menyimpan URI jarak jauh dan mem-proksi permintaan tersebut. Menyimpan URI ini baik-baik saja - ini setara dengan bookmark. Anda akan melakukan semua ini hanya untuk memiliki nama domain yang terpisah. Jujur saya tidak tahu mengapa Anda menginginkan ini - URI REST API Anda juga harus dapat bookmark.
Anda telah mengemukakan sebagian besar hal ini dalam pertanyaan Anda, tetapi saya merasa Anda telah menjebaknya dengan cara yang benar-benar tidak mengakui bahwa itu adalah cara praktis untuk melakukan apa yang ingin Anda lakukan (berdasarkan apa yang saya rasakan adalah batasan sewenang-wenang - bahwa API dan aplikasi terpisah). Dengan menanyakan apakah REST API tidak dapat menjadi back-end untuk aplikasi web, dan menyarankan bahwa kinerja akan menjadi masalah, saya pikir Anda memfokuskan semuanya pada hal-hal yang salah. Ini seperti mengatakan Anda tidak dapat membuat Mashup. Ini seperti mengatakan web tidak berfungsi.
sumber
my.web.app/pets/3
tanpa mengurai Url API REST'?my.web.app/pets/3
tanpa menguraikan URL sumber daya REST API yang sesuaimy.rest.api/v0/persons/2/pets/3
? Atau apa yang saya taruh di sana? '3
dalamapp/pets/3
karenaapp/pets/3
buram, ini menunjuk ke sumber daya apapun aplikasi web Anda inginkan. Jika itu adalah pandangan tersusun dari beberapa sumber daya lain (di sistem lain - API Anda adalah salah satunya) maka terserah Anda untuk menyimpan hyperlink ke sistem-sistem tersebut di dalam server aplikasi web, dan kemudian mengambilnya, menyelesaikannya ke representasi mereka ( misalnya JSON atau XML) dan kemudian sajikan sebagai bagian dari respons Anda.board/1
hal itufacebook.com/post/123
dantwitter.com/status/789
- ketika Anda pergi untuk memberikan representasi dari papan Anda, Anda harus menyelesaikan URI tersebut ke representasi yang dapat Anda kerjakan. Tembolok jika diperlukan.Kata pengantar
Jawaban ini secara khusus menjawab pertanyaan tentang bagaimana mengelola skema URL Anda sendiri termasuk URL bookmarkable unik untuk sumber daya yang API REST back-end tidak secara eksplisit mengekspos pengidentifikasi, dan tanpa menafsirkan URL yang disediakan oleh API.
Keterjangkauan membutuhkan sejumlah pengetahuan, jadi inilah pendapat saya tentang skenario dunia nyata:
Katakanlah kita menginginkan halaman pencarian di
http://my.web.app/person
mana hasilnya menyertakan tautan ke halaman detail untuk setiap orang. Satu hal kode front-end kami harus tahu dalam rangka untuk melakukan apa-apa adalah URL dasar untuk sumber data ISTIRAHAT nya:http://my.rest.api/api
. Respons terhadap permintaan GET untuk URL ini mungkin:Karena maksud kami adalah untuk menampilkan daftar orang, kami selanjutnya mengirimkan
GET
permintaan ke href dariperson
tautan href, yang mungkin kembali:Kami ingin menampilkan hasil pencarian, jadi kami akan menggunakan layanan pencarian dengan mengirimkan
GET
permintaan kesearch
tautan href, yang mungkin kembali:Kami akhirnya memiliki hasil kami, tetapi bagaimana kami membangun URL front-end kami?
Mari lepaskan bagian yang kita ketahui secara pasti: URL dasar API, dan gunakan sisanya sebagai pengidentifikasi ujung depan kami:
http://my.rest.api/api
http://my.rest.api/api/person/1
/person/1
http://my.web.app
http://my.web.app/person/1
Hasil kami mungkin terlihat seperti:
Setelah pengguna mengikuti tautan front-end itu ke halaman perincian, ke URL apa kami mengirim
GET
permintaan untuk perincian spesifik ituperson
? Kami tahu metode kami untuk memetakan URL back-end ke URL front-end, jadi kami balikkan saja:http://my.web.app/person/1
http://my.web.app
/person/1
http://my.rest.api/api
http://my.rest.api/api/person/1
Jika REST API berubah sedemikian rupa sehingga
person
URL sekaranghttp://my.rest.api/api/different-person-base/person/1
dan seseorang sebelumnya telah membuat bookmarkhttp://my.web.app/person/1
, REST API harus (setidaknya untuk sementara waktu) memberikan kompatibilitas ke belakang dengan menanggapi URL lama dengan mengarahkan ke yang baru. Semua tautan front-end yang dihasilkan akan mencakup struktur baru secara otomatis.Seperti yang mungkin Anda perhatikan, ada beberapa hal yang harus kita ketahui untuk menavigasi API:
person
hubungansearch
hubunganSaya tidak berpikir ada yang salah dengan ini; kami tidak mengasumsikan struktur URL tertentu pada titik mana pun, sehingga struktur URL entitas
http://my.rest.api/api/person/1
dapat berubah, dan selama API memberikan kompatibilitas ke belakang, kode kami akan tetap berfungsi.Anda bertanya bagaimana logika perutean kami dapat membedakan antara dua URL front-end:
http://my.rest.api/api/person/1
http://my.rest.api/api/pet/3
.Pertama-tama saya akan menunjukkan bahwa Anda menggunakan basis API dalam komentar Anda ketika dalam contoh kami menggunakan URL basis terpisah untuk UI dan REST API. Saya akan melanjutkan contoh menggunakan basis terpisah, tetapi berbagi basis tidak menjadi masalah. Kami dapat (atau seharusnya dapat) memetakan metode routing UI menggunakan jenis media dari header Terima permintaan.
Adapun perutean ke halaman detail tertentu, kami tidak dapat membedakan kedua URL tersebut jika kami ketat tentang menghindari pengetahuan tentang struktur
self
URL yang disediakan oleh API (yaitu id string buram). Untuk membuat ini berfungsi, mari sertakan info kami yang lain yang diketahui - jenis entitas yang kami kerjakan - di URL front-end kami.Sebelumnya URL front-end kami berada dalam format:
${UI base}/${opaque string id}
Format baru dapat:
${UI base}/${entity type}/${opaque string id}
Jadi dengan menggunakan
/person/1
contoh, kita akan berakhir denganhttp://my.web.app/person/person/1
.Dengan format ini, logika perutean UI kami akan berfungsi dengan baik
/person/person/1
, dan mengetahui bahwa token pertama dalam string dimasukkan oleh kami sendiri, kami dapat menariknya keluar dan merutekan ke halaman detail yang sesuai (orang, dalam contoh ini) berdasarkan pada itu. Jika Anda merasa malu dengan URL itu, maka kami dapat menyisipkan lebih banyak di sana; mungkin:http://my.web.app/person/detail/person/1
Dalam hal ini kita akan menguraikan
/person/detail
perutean untuk dan menggunakan sisanya sebagai id string buram.Saya menduga maksud Anda, karena URL front-end kami yang dihasilkan berisi bagian dari URL API, jika struktur URL API berubah tanpa mendukung struktur yang lama, kami akan memerlukan perubahan kode untuk menerjemahkan URL yang di-bookmark ke dalam versi baru URL API. Dengan kata lain, jika REST API mengubah ID sumber daya (string buram), maka kami tidak dapat berbicara dengan server tentang sumber daya itu menggunakan ID lama. Saya tidak berpikir kita bisa menghindari perubahan kode dalam situasi itu.
Anda dapat menggunakan struktur URL apa pun yang Anda inginkan. Pada akhirnya, URL yang dapat bookmark untuk sumber daya tertentu harus menyertakan sesuatu yang dapat Anda gunakan untuk mendapatkan URL API yang secara unik mengidentifikasi sumber daya itu. Jika Anda membuat pengenal Anda sendiri dan menyimpannya dengan URL API seperti pada pendekatan Anda # 3, itu akan berfungsi sampai seseorang mencoba menggunakan URL yang ditandai itu setelah entri itu dihapus dari cache.
Jawabannya tergantung pada hubungannya. Either way, Anda akan perlu cara untuk memetakan front-end ke URL API.
sumber
person/1
,pet/3
), lalu bagaimana ia tahu bahwa jika browser dibukahttp://my.rest.api/api/person/1
harus menunjukkan UI orang, dan jika terbukahttp://my.rest.api/api/pet/3
, lalu pet UI?Mari kita hadapi itu, tidak ada solusi ajaib. Sudahkah Anda membaca Richardson Maturity Model ? Ini membagi kematangan arsitektur REST menjadi 3 level: Sumber Daya, kata kerja HTTP, dan kontrol hypermedia.
Ini adalah kontrol hypermedia. Apakah Anda benar-benar membutuhkannya? Pendekatan ini memiliki beberapa manfaat yang sangat baik (Anda dapat membacanya di sini ). Tetapi tidak ada yang namanya makanan gratis dan Anda harus bekerja keras (mis. Solusi kedua) jika Anda ingin mendapatkannya.
Ini adalah masalah keseimbangan - apakah Anda ingin mengorbankan kinerja (dan membuat kode Anda lebih rumit) tetapi mendapatkan sistem yang lebih fleksibel? Atau apakah Anda lebih suka menjaga hal-hal lebih cepat dan sederhana tetapi membayar nanti ketika Anda memperkenalkan perubahan pada api / model Anda?
Sebagai seseorang yang mengembangkan sistem serupa (tier logika bisnis, tier web, dan klien web), saya memilih opsi kedua. Karena grup saya mengembangkan semua tingkatan, kami memutuskan lebih baik memiliki sedikit sambungan (dengan memberi tahu web tier tentang id entitas dan membangun url api) dan sebagai gantinya mendapatkan kode yang lebih sederhana. Kompatibilitas mundur juga tidak relevan dalam kasus kami.
Jika aplikasi web dikembangkan oleh pihak ke-3 atau jika kompatibilitas ke belakang adalah masalah, kami mungkin telah memilih secara berbeda karena ada nilai yang besar untuk dapat mengubah struktur url tanpa mengubah aplikasi web. Cukup untuk menjustifikasi penyulit kode.
Saya pikir itu berarti bahwa Anda tidak harus membuat implementasi REST yang sempurna. Anda dapat menggunakan solusi kedua Anda, atau memaparkan entitas id atau mungkin meneruskan url api . Tidak apa-apa selama Anda memahami implikasi dan trade-off.
sumber
Saya kira jika Anda tetap pada sesuatu yang mirip dengan
Atom Syndication Format
Anda baik.Di sini metadata yang menjelaskan Entri yang diwakili dapat ditentukan menggunakan elemen / atribut tambahan:
Sesuai [RFC4287] , berisi URI yang secara unik mengidentifikasi Entri
Sesuai [RFC4287] , elemen ini opsional. Jika disertakan, itu berisi URI yang harus digunakan klien untuk mengambil Entri.
Ini hanya dua sen saya.
sumber
Jangan khawatir tentang URL, khawatir tentang jenis media.
Lihat di sini (titik peluru ketiga khususnya).
Dalam kasus aplikasi web biasa, klien adalah manusia ; browser hanyalah agen .
Jadi tag jangkar suka
sesuai dengan sesuatu seperti
URL masih buram bagi pengguna, yang ia pedulikan hanyalah jenis media (mis
text/html, application/pdf, application/flv, video/x-flv, image/jpeg, image/funny-cat-picture etc
.). Teks deskriptif yang terkandung dalam jangkar (dan dalam atribut judul) hanyalah cara untuk memperluas jenis hubungan dengan cara yang dapat dipahami manusia.Alasan Anda ingin URI buram bagi klien adalah agar Anda mengurangi penggandengan (salah satu tujuan utama REST). Server dapat mengubah / mengatur ulang URI tanpa mempengaruhi klien (selama Anda memiliki kebijakan caching yang baik - yang mungkin berarti tidak ada caching sama sekali).
Singkatnya
Pastikan klien (manusia atau mesin) peduli dengan jenis media dan hubungannya daripada URL dan Anda akan baik-baik saja.
sumber
Saya pikir Anda benar, itu adalah cara paling sederhana. Anda dapat merelatifkan URL
http://my.rest.api/api
agar tidak jelek:Jika URL yang disediakan oleh API tidak terjadi relatif terhadap basis itu, maka akan menurun ke bentuk jelek:
Untuk melangkah lebih jauh, periksa respons dari server API untuk menentukan jenis tampilan yang ingin Anda sajikan dan berhenti menyandikan pembatas segmen dan titik dua segmen jalan:
sumber