Aplikasi Web sebagai klien REST API: cara menangani pengidentifikasi sumber daya

21

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 hrefatribut? Bagaimana cara saya menjaga URL entitas API di aplikasi web agar bisa mendapatkan entitas ketika pengguna membuka halaman target?

Persyaratannya tampaknya saling bertentangan:

  1. Hyperlink hrefharus mengarah ke aplikasi web karena itu adalah sistem hosting UI
  2. The hrefharus memiliki beberapa id entitas karena aplikasi web harus mampu mengatasi entitas ketika halaman target terbuka
  3. 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 1234dari 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%2F1234itu 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:

  1. 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.
  2. 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.
  3. Simpan semua selfURL 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 menyimpan http://my.rest.api/pet/678URL di suatu tempat dengan kunci baru, katakanlah 3, dan hasilkan URL halaman web sebagai http://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?

Pavel Gatilov
sumber
1
Tidak jelas apa yang ingin Anda capai, mungkin karena niat sederhana Anda tercakup di bawah lapisan arsitektur yang Anda tempatkan satu sama lain, jadi sulit untuk mengatakan apakah "RESTful APIs" benar-benar akan membantu Anda. Dari apa yang saya pahami masalah Anda, opsi 2 adalah solusi yang sederhana dan bisa diterapkan. "Masalah" di sini melekat pada "RESTful APIs". RestIsJustSqlReinvented dan Anda memang akan mengalami masalah yang sama ketika Anda mencoba untuk mengambil subgraph yang cukup kompleks dari RDBMS. Gunakan cache atau representasi yang dioptimalkan untuk kueri Anda.
back2dos

Jawaban:

5

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:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/pets/1" />
    <Link rel="owner" href="http://example.com/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

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:

http://example.com/GUI/pets/Spot

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:

http://example.com/GUI/owners/John/pets/1 (hewan peliharaan pertama dalam daftar untuk John)

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:

<Entity type="ResourceList">
    <Link rel="people" href="http://example.com/api/people" />
    <Link rel="pets" href="http://example.com/api/pets" />
</Entity>

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:

<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/1" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/1" />
        <Link rel="pet" href="http://example.com/api/pets/2" />
    </Pets>
    <UniqueName>John</UniqueName>
</Entity>
<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/2" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/3" />
    </Pets>
    <UniqueName>Jane</UniqueName>
</Entity>

GUI akan mengulangi setiap sumber daya dan mencetak item daftar untuk setiap orang menggunakan UniqueName sebagai "id":

<a href="http://example.com/gui/people/1">John</a>
<a href="http://example.com/gui/people/2">Jane</a>

Saat melakukan ini, ia juga dapat memproses setiap tautan yang ditemukannya dengan rel "pet" dan mendapatkan sumber daya pet seperti:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/api/pets/1" />
    <Link rel="owner" href="http://example.com/api/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

Dengan ini, ia dapat mencetak tautan seperti:

<!-- Assumes that a pet can exist without an owner -->
<a href="http://example.com/gui/pets/Spot">Spot</a>

atau

<!-- Assumes that a pet MUST have an owner -->
<a href="http://example.com/gui/people/John/pets/Spot">Spot</a>

Jika kita menggunakan tautan pertama dan menganggap bahwa sumber entri kami memiliki tautan dengan relasi "hewan peliharaan", aliran kontrol akan seperti ini di GUI:

  1. Halaman dibuka dan Spot hewan peliharaan diminta.
  2. Muat daftar sumber daya dari titik masuk API.
  3. Muat sumber daya yang terkait dengan istilah "hewan peliharaan".
  4. Lihat setiap sumber daya dari respons "hewan peliharaan" dan temukan yang cocok dengan Spot.
  5. Tampilkan informasi untuk spot.

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.

Mike
sumber
Terima kasih, Mike. Saya telah memperbarui pertanyaan saya untuk membuatnya lebih jelas. Masalah dengan jawaban Anda adalah bahwa saya tidak dapat menyetujui bahwa klien REST dapat menguraikan URL. Jika ya, maka akan digabungkan ke URL. Dan ini melanggar salah satu ide inti REST: klien harus menggunakan rels untuk memilih tautan, tetapi tidak boleh mengasumsikan pengetahuan tentang struktur URL. REST menegaskan bahwa API bebas untuk mengubah URL sesuka hati asalkan reltetap sama. Parsing URL menggeser kami lebih dekat ke SOAP daripada ke REST.
Pavel Gatilov
Terima kasih lagi. Anda telah menggambarkan pendekatan yang telah kami lakukan sejauh ini. Di satu sisi, kami mengekspos pengidentifikasi. Satu-satunya hal adalah bahwa kami mencoba untuk mengekspos pengidentifikasi alami bila memungkinkan.
Pavel Gatilov
6

Apakah itu semua berarti bahwa RESTful APIs tidak dapat berfungsi sebagai backend untuk aplikasi web?

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/...dan http://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/1ia 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.

Doug
sumber
Saya tidak berharap aplikasi web hanya menjadi representasi untuk api. Ini dapat memiliki banyak perbedaan, misalnya menunjukkan beberapa sumber daya anak bersama-sama dengan beberapa root pada satu halaman. Saya tidak ingin url aplikasi web mengandung id internal penyimpanan data api, jika Anda maksudkan ini dengan mengatakan bahwa saya berharap 2 sistemnya sama. Saya tidak peduli dengan kinerja di sini, itu bukan masalah. Pertanyaannya sebenarnya adalah 'Bagaimana cara memasukkan 3 ke dalam my.web.app/pets/3tanpa mengurai Url API REST'?
Pavel Gatilov
Mengoreksi pengungkapan ulang saya sendiri: 'Bagaimana cara memasukkan 3 ke dalam my.web.app/pets/3tanpa menguraikan URL sumber daya REST API yang sesuai my.rest.api/v0/persons/2/pets/3? Atau apa yang saya taruh di sana? '
Pavel Gatilov
Saya pikir Anda membingungkan keadaan klien dengan representasi yang menentukan keadaan itu. Anda tidak memasukkan 3dalam app/pets/3karena app/pets/3buram, 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.
Doug
Pikirkan seperti ini - lupakan API dan Aplikasi Anda. Asumsikan Anda ingin membuat situs yang memungkinkan orang mengumpulkan posting Facebook dan Twitter favorit mereka. Itu adalah sistem jarak jauh. Anda tidak akan mencoba untuk tunnel atau template URI ke sistem tersebut melalui Anda sendiri. Anda akan membuat sumber daya 'papan' dan itu akan menjadi server Anda yang mengetahui board/1hal itu facebook.com/post/123dan twitter.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.
Doug
Jadi, karena Anda ingin API Anda berbeda secara signifikan dari aplikasi Anda (saya pikir ini masih dipertanyakan) - memperlakukannya seperti sistem jarak jauh menjadi tidak berbeda dari itu. Anda mengatakan kinerja bukan masalah, tetapi Anda juga mengatakan dalam pertanyaan Anda bahwa sesuatu seperti ini akan 'mempengaruhi kinerja'.
Doug
5

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/personmana 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:

<Links>
    <Link ref="self" href="http://my.rest.api/api" />
    <Link rel="person" href="http://my.rest.api/api/person" />
    <Link rel="pet" href="http://my.rest.api/api/pet" />
</Links>

Karena maksud kami adalah untuk menampilkan daftar orang, kami selanjutnya mengirimkan GETpermintaan ke href dari persontautan href, yang mungkin kembali:

<Links>
    <Link ref="self" href="http://my.rest.api/api/person" />
    <Link rel="search" href="http://my.rest.api/api/person/search" />
</Links>

Kami ingin menampilkan hasil pencarian, jadi kami akan menggunakan layanan pencarian dengan mengirimkan GETpermintaan ke searchtautan href, yang mungkin kembali:

<Persons>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/1"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/10"/>
        </Pets>
    </Person>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/2"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/20"/>
        </Pets>
    </Person>
</Persons>

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:

  • basis API yang dikenal: http://my.rest.api/api
  • URL yang diberikan untuk masing-masing entitas: http://my.rest.api/api/person/1
  • identitas unik: /person/1
  • URL dasar kami: http://my.web.app
  • URL front-end kami yang dihasilkan: http://my.web.app/person/1

Hasil kami mungkin terlihat seperti:

<ul>
    <li><a href="http://my.web.app/person/1">A person</a></li>
    <li><a href="http://my.web.app/person/2">A person</a></li>
</ul>

Setelah pengguna mengikuti tautan front-end itu ke halaman perincian, ke URL apa kami mengirim GETpermintaan untuk perincian spesifik itu person? Kami tahu metode kami untuk memetakan URL back-end ke URL front-end, jadi kami balikkan saja:

  • URL front-end: http://my.web.app/person/1
  • URL dasar kami: http://my.web.app
  • identitas unik: /person/1
  • basis API yang dikenal: http://my.rest.api/api
  • URL API yang dihasilkan: http://my.rest.api/api/person/1

Jika REST API berubah sedemikian rupa sehingga personURL sekarang http://my.rest.api/api/different-person-base/person/1dan seseorang sebelumnya telah membuat bookmark http://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:

  • URL dasar API
  • yang personhubungan
  • yang searchhubungan

Saya 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/1dapat 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 selfURL 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/1contoh, kita akan berakhir dengan http://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/detailperutean untuk dan menggunakan sisanya sebagai id string buram.


Saya pikir ini memperkenalkan kopling aplikasi web yang sangat ketat ke api.

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.

Bagaimana jika saya ingin struktur URL untuk aplikasi web berbeda dari api?

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.

Bagaimana jika entitas aplikasi web saya tidak memetakan ke entitas api 1-1?

Jawabannya tergantung pada hubungannya. Either way, Anda akan perlu cara untuk memetakan front-end ke URL API.

Mike Partridge
sumber
Saya punya satu masalah dengan pendekatan ini. Ini sebenarnya nomor 1 dalam daftar solusi saya. Yang tidak saya dapatkan adalah ini: jika aplikasi web tidak menafsirkan URL dan memperlakukan id unik sebagai string buram (hanya person/1, pet/3), lalu bagaimana ia tahu bahwa jika browser dibuka http://my.rest.api/api/person/1harus menunjukkan UI orang, dan jika terbuka http://my.rest.api/api/pet/3, lalu pet UI?
Pavel Gatilov
Pertanyaan bagus! Saya telah memperbarui jawaban dengan tanggapan saya.
Mike Partridge
Terima kasih, Mike. Saya pikir ini memperkenalkan kopling aplikasi web yang sangat ketat ke api. Bagaimana jika saya ingin struktur URL untuk aplikasi web berbeda dari api? Bagaimana jika entitas aplikasi web saya tidak memetakan ke entitas api 1-1? Saya masih berpikir bahwa saya sebaiknya mengambil pendekatan mengungkap beberapa pengidentifikasi, tetapi mendesak klien untuk menggunakan tautan untuk navigasi.
Pavel Gatilov
Ini adalah topik yang menarik, jadi saya harap saya tidak melewatkan apa pun. Saya telah memperbarui jawaban saya dengan tanggapan terhadap komentar Anda. Saya pikir mengekspos beberapa pengidentifikasi adalah kompromi yang baik antara RESTfulness lengkap dan kegunaan.
Mike Partridge
Perhatian utama saya di sini sedikit lebih praktis. Saya menggunakan ASP.NET MVC untuk mengimplementasikan aplikasi web, dan karena beberapa aturan internal saya harus mendefinisikan pola url yang didukung oleh aplikasi. Yaitu jika / a / {id} didefinisikan, maka aplikasi akan menangani / a / 1, tetapi tidak / a / 1 / b / 2. Ini mengarah pada persyaratan untuk mengkompilasi ulang aplikasi web jika url REST API berubah tidak hanya untuk mempertahankan URL yang di-bookmark, tetapi juga untuk membuat aplikasi web berfungsi saat bernavigasi dari root. Hanya karena hyperlink yang disematkan ke halaman html tidak akan berfungsi tanpanya.
Pavel Gatilov
2

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.

Saya tidak boleh mengekspos pengidentifikasi mentah entitas saya, melainkan mengembalikan hyperlink dengan rel = "self"

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.

Apakah itu semua berarti bahwa RESTful APIs tidak dapat berfungsi sebagai backend untuk aplikasi web?

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.

daramasala
sumber
0

Saya kira jika Anda tetap pada sesuatu yang mirip dengan Atom Syndication FormatAnda 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.

rusev
sumber
Mungkin saya tidak mendapatkan sesuatu, tetapi menurut saya jawaban Anda tidak menjelaskan cara membuat URL aplikasi web yang merupakan klien ke REST API, bukan?
Pavel Gatilov
0

Jangan khawatir tentang URL, khawatir tentang jenis media.

Lihat di sini (titik peluru ketiga khususnya).

REST API harus menghabiskan hampir semua upaya deskriptifnya dalam mendefinisikan jenis media yang digunakan untuk mewakili sumber daya dan status aplikasi mengemudi, atau dalam mendefinisikan nama hubungan yang diperluas dan / atau mark-up yang diaktifkan hypertext untuk jenis media standar yang ada. .


Dalam kasus aplikasi web biasa, klien adalah manusia ; browser hanyalah agen .

Jadi tag jangkar suka

          <a href="example.com/foo/123">click here</a>

sesuai dengan sesuatu seperti

          <link type="text/html" rel="self" href="example.com/foo/123">

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.

Rodrick Chapman
sumber
Rodrick, pertanyaan saya bukan tentang membangun API, melainkan tentang membangun aplikasi web yang berada di atas API TENANG. Saya hampir tidak dapat memahami bagaimana jenis media dapat membantu saya membuat URL untuk aplikasi web. Meskipun jenis media sangat penting untuk kontrak layanan dan dapat ditemukan.
Pavel Gatilov
@PavelGatilov - Apakah klien aplikasi web Anda seorang manusia?
Rodrick Chapman
Ya itu. Dan yang sangat kurang terampil.
Pavel Gatilov
0

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 jelek.

Saya pikir Anda benar, itu adalah cara paling sederhana. Anda dapat merelatifkan URL http://my.rest.api/apiagar tidak jelek:

http://my.web.app/person/person%2F1234

Jika URL yang disediakan oleh API tidak terjadi relatif terhadap basis itu, maka akan menurun ke bentuk jelek:

http://my.web.app/person/http%3A%2F%2Fother.api.host%2Fapi%2Fperson%2F1234

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:

http://my.web.app/person/1234 (best case)
http://my.web.app/http://other.api.host/api/person/1234 (ugly case)
kecil
sumber