Веб-додаток як клієнт REST API: як обробляти ідентифікатори ресурсів


21

Кілька концепцій, пов'язаних з REST, конфліктують в моїй голові, коли я намагаюся його реалізувати.

У мене є REST-повна система API, що підтримує бізнес-логіку, та веб-додаток, що забезпечує інтерфейс користувача. З різних ресурсів про REST (зокрема, REST в практиці: гіпермедіа та архітектура систем ) я знаю, що я не повинен виставляти необроблені ідентифікатори моїх сутностей, а швидше повертати гіперпосилання за допомогою rel="self".

Розглянемо приклад. АРІЙТЕ api має ресурс, який повертає людину:

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

Проблема виникає з веб-додатком. Припустимо, він повертає сторінку, що містить гіперпосилання на браузери:

<body class="person">
  <p>
    <a href="http://my.web.app/pet/???????" />
  </p>
</body>

Що я повинен помістити в hrefатрибут? Як я можу зберігати URL-адресу сутності API у веб-програмі, щоб мати змогу отримати об'єкт, коли користувач відкриває цільову сторінку?

Вимоги здаються суперечливими:

  1. Гіперпосилання hrefмає призвести до веб-програми, оскільки це система, що розміщує інтерфейс користувача
  2. hrefПовинні мати ідентифікатор об'єкта , так як веб - додаток повинен мати можливість звернутися до суті , коли відкриється цільова сторінка
  3. Веб-додаток не повинен аналізувати / створювати REST URL-адреси, оскільки це не є REST-ful, йдеться у згаданій книзі

URI повинні бути непрозорими для споживачів. Тільки емітент URI знає, як його інтерпретувати та відображати на ресурсі.

Отже, я не можу просто взяти 1234URL-адресу відповіді API, тому що як RESTful клієнт я повинен ставитися до нього так, ніби це щось подібне http://my.rest.api/api/AGRIDd~ryPQZ^$RjEL0j. З іншого боку, я повинен надати певну URL-адресу, яка веде до мого веб-додатку, і цього достатньо, щоб програма якось відновила початкову URL-адресу API та використовувала цю URL-адресу для доступу до ресурсів API.

Найпростіший спосіб - це, мабуть, лише використання URL-адрес API як їх рядкових ідентифікаторів. Але такі URL-адреси веб-сторінок http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234некрасиві.

Все це здається досить простим для настільного додатку або односторінкової програми javascript. Оскільки вони живуть постійно, вони можуть просто зберігати URL-адреси в пам’яті разом із об’єктами служби протягом усього життя програми та використовувати їх у разі необхідності.

За допомогою веб-програми я можу уявити кілька підходів, але все здається дивним:

  1. Замініть хост у URL-адресах API і збережіть лише результат. Величезний мінус полягає в тому, що він вимагає від веб-програми обробляти будь-яку URL-адресу, що створюється API, що означає жахливе з'єднання. Більше того, це знову не RESTful, оскільки мій веб-додаток починає інтерпретувати URL-адреси.
  2. Розкрийте необроблені ідентифікатори в API REST разом із посиланнями, використовуйте їх для створення URL-адрес веб-додатків, а потім використовуйте ідентифікатори на сервері веб-додатків, щоб знайти потрібні ресурси в API. Це краще, але це вплине на ефективність роботи сервера веб-додатків, оскільки веб-додатку доведеться пройти навігаційну службу REST, видаючи ланцюжок запитів на отримання ідентифікації певної форми для обробки будь-якого запиту браузера. Для дещо вкладеного ресурсу це може бути дорогим.
  3. Зберігати всі selfURL-адреси, повернені api, у постійному (DB?) Зіставленні на сервері веб-додатків. Створіть для них деякі ідентифікатори, використовуйте ідентифікатори для створення URL-адрес сторінки веб-додатків та отримання URL-адрес ресурсів служби REST. Тобто я зберігаю http://my.rest.api/pet/678URL-адресу десь за допомогою нового ключа, скажімо 3, і генерую URL-адресу веб-сторінки як http://my.web.app/pet/3. Це схоже на якусь реалізацію кеша HTTP. Не знаю чому, але мені це здається дивним.

Або все це означає, що API RESTful не можуть служити основою для веб-додатків?


1
Незрозуміло, що ви намагаєтеся досягти, ймовірно, тому, що ваш простий намір прикритий під шарами архітектури, які ви накладаєте один на одного, тому важко сказати, чи дійсно «RESTful API» допоможе вам. З того, що я розумію у вашій проблемі, варіант 2 - це просте та ефективне рішення. Тут "проблема" притаманна "API RESTful". RestIsJustSqlReinvented, і ви дійсно зіткнетесь з тією ж проблемою, коли спробуєте отримати досить складний підграф з будь-якої RDBMS. Використовуйте кеш або представлення, оптимізоване для ваших запитів.
back2dos

Відповіді:


5

Відредаговано для оновлення запитань, попередня відповідь видалена

Переглядаючи ваші зміни до вашого питання, я думаю, я розумію проблему, з якою ви стикаєтеся трохи більше. Оскільки на ваших ресурсах не існує поля, яке є ідентифікатором (лише посилання), ви не можете посилатися на цей конкретний ресурс у вашому графічному інтерфейсі (тобто посилання на сторінку, що описує конкретного домашнього улюбленця).

Перше, що потрібно визначити, чи не має домашнього улюбленця сенс без господаря. Якщо ми можемо мати домашнього улюбленця без жодного власника, то я б сказав, що нам потрібна якась унікальна властивість для домашнього улюбленця, яку ми можемо використати для позначення цього. Я не вірю, що це порушило б не відкриття ідентифікатора безпосередньо, оскільки фактичний ідентифікатор ресурсу все одно буде затягнутий у посилання, яке REST-клієнт не розбере. Зважаючи на це, наш ресурс домашніх тварин може виглядати так:

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

Тепер ми можемо оновити ім'я цього домашнього улюбленця від Spot до Fido, не маючи воювати з будь-якими власними ідентифікаторами ресурсів у всій програмі. Так само ми можемо звернутися до цього вихованця в нашому графічному інтерфейсі з чимось на зразок:

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

Якщо домашня тварина не має сенсу без власника (або домашні тварини заборонені в системі без власника), тоді ми можемо використовувати власника як частину "ідентичності" вихованця в системі:

http://example.com/GUI/owners/John/pets/1 (перший вихованець у списку для Джона)

Одне невелике зауваження, якщо і домашні тварини, і люди можуть існувати окремо один від одного, я б не робив точкою входу для API ресурс "Люди". Натомість я створив би більш загальний ресурс, який би містив посилання на людей та домашніх тварин. Він може повернути ресурс, який виглядає так:

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

Отже, знаючи лише першу точку введення в API і не обробляючи жодної URL-адреси, щоб з'ясувати системні ідентифікатори, ми можемо зробити щось подібне:

Користувач входить у програму. Клієнт REST отримує доступ до всього списку доступних людських ресурсів, який може виглядати так:

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

Графічний інтерфейс буде проходити цикл через кожен ресурс і роздруковувати елемент списку для кожної людини, використовуючи UniqueName як "id":

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

Під час цього він також може обробити кожне посилання, яке він знайде, з відношенням "pet" та отримати ресурс для домашніх тварин, наприклад:

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

Використовуючи це, він може надрукувати посилання, наприклад:

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

або

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

Якщо ми переходимо за першим посиланням і припускаємо, що наш ресурс введення має посилання на відношення "домашніх тварин", то керуючий потік буде щось подібне в графічному інтерфейсі:

  1. Сторінка відкрита, і домашня тварина Spot запитується.
  2. Завантажте список ресурсів з точки входу API.
  3. Завантажте ресурс, пов'язаний з терміном "домашні тварини".
  4. Перегляньте кожен ресурс із відповіді "домашніх тварин" і знайдіть той, який відповідає Spot.
  5. Виведіть інформацію про місце.

Використання другого посилання було б подібним ланцюжком подій, за винятком того, що Люди є вхідною точкою API, і ми спочатку отримаємо список усіх людей у ​​системі, знайдемо відповідного, а потім знайдемо всіх домашніх тварин, які належать до цієї особи (знову використовуючи тег rel) і знайдіть той, який названий Spot, щоб ми могли відобразити конкретну інформацію, пов’язану з ним.


Спасибі, Майку. Я оновив своє запитання, щоб зробити його більш зрозумілим. Проблема з вашою відповіддю полягає в тому, що я не можу погодитися, що клієнт REST може проаналізувати URL-адреси. Якщо це так, то він приєднується до URL-адрес. І це порушує одну з основних ідей REST: клієнти повинні використовувати rels для вибору посилань, але не повинні припускати будь-яких знань про структуру URL-адрес. REST стверджує, що API може вільно змінювати URL-адреси за бажанням, якщо вони relзалишаться однаковими. Розбір URL-адрес переміщує нас ближче до SOAP, ніж до REST.
Павло Гатилов

Ще раз дякую вам. Ви описали підхід, який ми застосували досі. Деяким чином ми виставляємо ідентифікатори. Єдине, що ми намагаємось розкривати природні ідентифікатори, коли це можливо.
Павло Гатилов

6

Чи все це означає, що API RESTful не можуть служити основою для веб-додатків?

Я заперечую, чи варто розрізняти REST API і веб-додаток. Ваш «веб - додаток» має бути просто альтернативні (HTML) уявлення одних і тих же ресурсів - що сказати, я не розумію , як і чому ви очікуєте доступу http://my.rest.api/...і , http://my.web.app/...і що вони будуть одночасно однакові і різні.

У цьому випадку ваш "клієнт" - це браузер, і він розуміє HTML та JavaScript. На мою думку, це веб-додаток. Тепер ви можете не погодитись і подумати, що ви звертаєтесь до зазначеного веб-додатку за допомогою foo.com та відкриваєте все інше за допомогою api.foo.com - але тоді ви повинні запитати, як foo.com надав мені представлення ресурсу? "Бек-енд" foo.com цілком здатний зрозуміти, як знайти ресурси з api.foo.com. Ваш веб-додаток просто став проксі-сервером - не чим іншим, як якщо б ви разом спілкувалися з іншим API (від когось іншого).

Тож ваше запитання можна узагальнити до "Як я можу описати ресурси, використовуючи власні URI, які існують в інших системах?" що тривіально, якщо ви вважаєте, що не клієнт (HTML / JavaScript) повинен розуміти, як це зробити, а сервер. Якщо ви погоджуєтесь з моїм першим викликом, тоді ви можете просто розглянути свій веб-додаток як окремий API REST, який надає проксі-сервер або делегує інший REST API.

Тож, коли ваш клієнт має доступ, my.web.app/pets/1він знає, що він повинен представити інтерфейс для домашніх тварин, оскільки або це повернуто шаблоном на стороні сервера, або якщо це асинхронний запит для іншого представлення (наприклад, JSON або XML), заголовок типу вмісту говорить про це .

Сервер, який це забезпечує, - це той, хто відповідає за розуміння того, що таке домашня тварина, і як виявити домашню тварину на віддаленій системі. Як ви це зробите, залежати від вас - ви можете просто взяти ідентифікатор і створити інший URI, що, на вашу думку, є недоречним, або ви можете мати власну базу даних, яка зберігає віддалений URI і надає проксі. Зберігання цього URI чудово - це рівнозначно закладці. Ви б робили все це лише для того, щоб мати окреме доменне ім’я. Я не чесно знаю, чому ви цього хочете - ваші URI-файли REST API також мають бути доступними для закладок.

Ви вже підняли більшість цього питання у своєму питанні, але я вважаю, що ви поставили це так, що насправді не визнали, що це практичний спосіб робити те, що ви хочете робити (виходячи з того, що я відчуваю, це довільне обмеження - щоб API та програма були окремими). Запитуючи, чи API API REST не може бути зворотним для веб-додатків, і припускаючи, що продуктивність буде проблемою, я думаю, що ви зосереджуєтесь на неправильних речах. Це як сказати, що ви не можете створити Mashup. Це як би сказати, що Інтернет не працює.


Я не очікую, що веб-додаток буде просто представництвом для api. Це може мати багато відмінностей, наприклад, показати кілька дочірніх ресурсів разом з деяким кореневим на одній сторінці. Я не хочу, щоб веб-додатки містили внутрішні ідентифікатори зберігання даних api, якщо ви це маєте на увазі, кажучи, що я очікую, що дві системи будуть однаковими. Я тут не переймаюся продуктивністю, це не проблема. Питання насправді "Як я можу ввести 3 my.web.app/pets/3без розбору URL-адрес REST API"?
Павло Гатилов

Виправлення власного переформулювання: "Як я можу вставити 3, my.web.app/pets/3не розбираючи URL-адресу відповідного ресурсу REST API my.rest.api/v0/persons/2/pets/3? Або що я туди поклав? '
Павло Гатилов

Я думаю, ви плутаєте стан клієнта з уявленнями, які визначають цей стан. Ви не пишіть 3в app/pets/3тому app/pets/3непрозорий, він вказує на те , що ресурс вашого веб - додаток хоче. Якщо це складене уявлення про декілька інших ресурсів (в інших системах - ваш API є одним із них), то саме вам належить зберігати гіперпосилання на ці системи на сервері веб-додатків, а потім отримувати їх, вирішувати їх на їх представленнях ( наприклад, JSON або XML), а потім подавати їх як частину вашої відповіді.
Дуг

Подумайте про це так - забудьте свій API та додаток. Припустимо, ви хотіли створити сайт, який дозволяє людям збирати свої улюблені пости у Facebook та Twitter. Це віддалені системи. Ви не збираєтеся пробувати тунелі або шаблонувати URI-адреси в ці системи через вашу власну. Ви б створили ресурс "дошки", і саме ваш сервер знає, що це board/1вказує, facebook.com/post/123і twitter.com/status/789- коли ви збираєтеся представити свою дошку, вам доведеться вирішити ці URI в представництві, з яким ви можете працювати. Кеш, де потрібно.
Дуг

І так, оскільки ви хочете, щоб ваш API значно відрізнявся від вашої програми (я все-таки думаю, що це сумнівно) - поводження з ним як із віддаленою системою стає не чим іншим. Ви сказали, що ефективність не є проблемою, але ви також сказали у своєму запитанні, що щось подібне вплине на ефективність роботи.
Дуг

5

Передмова

Ця відповідь конкретно стосується питання про те, як керувати власною схемою URL-адрес, включаючи унікальні URL-адреси, що дозволяють розміщувати закладки для ресурсів, для яких резервний API REST явно не виставляє ідентифікатор та без інтерпретації URL-адрес, наданих API.


Виявлення вимагає певного обсягу знань, тож ось я ставлюсь до сценарію реального світу:

Скажімо, нам потрібна сторінка пошуку, http://my.web.app/personде результати містять посилання на сторінку деталей для кожної людини. Одна річ , наш передній кінець коду повинні знати, щоб зробити що - небудь взагалі є базовим URL для джерела даних REST: http://my.rest.api/api. Відповідь на запит GET на цю URL-адресу може бути:

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

Оскільки наша мета полягає у відображенні списку людей, ми наступним чином надсилаємо GETзапит href із personпосилання href, яке може повернутися:

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

Ми хочемо відобразити результати пошуку, тому ми скористаємося службою пошуку, надіславши GETзапит на searchпосилання href, яке може повернути:

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

Нарешті ми маємо свої результати, але як ми будуємо наші передні URL-адреси?

Давайте знімемо частину, про яку ми точно знаємо: базову URL-адресу API, а решту використаємо як наш ідентифікатор на передньому рівні:

  • відома база API: http://my.rest.api/api
  • вказана URL-адреса для фізичної особи: http://my.rest.api/api/person/1
  • унікальний ідентифікатор: /person/1
  • наша основна URL-адреса: http://my.web.app
  • наша генерована передня URL-адреса: http://my.web.app/person/1

Наші результати можуть виглядати так:

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

Як тільки користувач переходить по передньому посиланню на сторінку деталей, до якої URL-адреси ми надсилаємо GETзапит на деталі цього конкретного person? Ми знаємо наш спосіб зіставлення зворотних URL-адрес у передні URL-адреси, тому ми просто реверсуємо його:

  • передня URL-адреса: http://my.web.app/person/1
  • наша основна URL-адреса: http://my.web.app
  • унікальний ідентифікатор: /person/1
  • відома база API: http://my.rest.api/api
  • створена URL-адреса API: http://my.rest.api/api/person/1

Якщо API REST змінюється таким чином, що personURL-адреса зараз є, http://my.rest.api/api/different-person-base/person/1а хтось раніше робив закладки http://my.web.app/person/1, API REST повинен (принаймні на час) забезпечити сумісність із зворотним зв'язком, відповідаючи на стару URL-адресу з перенаправленням на нову. Усі генеровані передні посилання включають нову структуру автоматично.

Як ви, напевно, помітили, ми маємо знати кілька речей, щоб орієнтуватися в API:

  • базову URL-адресу API
  • personспіввідношення
  • searchспіввідношення

Я не думаю, що в цьому нічого поганого; ми не передбачаємо конкретної структури URL-адреси в будь-якій точці, тому структура URL-адреси сутності http://my.rest.api/api/person/1може змінюватися, і поки API надає зворотну сумісність, наш код все одно буде працювати.


Ви запитали, як наша логіка маршрутизації може визначити різницю між двома передніми URL-адресами:

  • http://my.rest.api/api/person/1
  • http://my.rest.api/api/pet/3.

Спершу зазначу, що ви використовували базу API у своєму коментарі, коли в нашому прикладі ми використовуємо окремі базові URL-адреси для інтерфейсу користувача та API REST. Я продовжую приклад, використовуючи окремі бази, але обмін базою не є проблемою. Ми можемо (або повинні вміти) відображати способи маршрутизації інтерфейсу користувача, використовуючи тип носія з заголовка Accept запиту.

Що стосується маршрутизації до певної сторінки деталізації, ми не можемо розмежувати ці дві URL-адреси, якщо ми суворо ставимось до уникнення будь-яких знань про структуру selfURL-адреси, наданої API (тобто непрозорий ідентифікатор рядка). Щоб зробити цю роботу, включимо ще одну з відомих нам відомостей - тип сутності, з якою ми працюємо - в наші передні URL-адреси.

Раніше наші передні URL-адреси були у форматі: ${UI base}/${opaque string id}

Новий формат може бути: ${UI base}/${entity type}/${opaque string id}

Отже, використовуючи /person/1приклад, ми закінчилися http://my.web.app/person/person/1.

З цим форматом працюватиме наша логіка маршрутизації інтерфейсу користувача /person/person/1, і, знаючи, що перший маркер у рядку ми вставили самі, ми можемо витягнути його та перенести на відповідну (особу, у цьому прикладі) сторінку деталей на його основі. Якщо ви відчуваєте дурність щодо цієї URL-адреси, тому ми можемо вставити трохи більше; можливо: http://my.web.app/person/detail/person/1

У такому випадку ми би розбирали /person/detailдля маршрутизації і решту використовували як непрозорий ідентифікатор рядка.


Я думаю, що це вводить надзвичайно щільне з'єднання веб-програми з api.

Я маю на увазі, що ви маєте на увазі, що оскільки наша створена передня URL-адреса містить частину URL-адреси API, якщо структура URL-адреси API змінюється, не підтримуючи стару структуру, нам знадобиться зміна коду, щоб перевести URL-адресу, що розмістила закладку нова версія URL-адреси API. Іншими словами, якщо API REST змінює ідентифікатор ресурсу (непрозорий рядок), ми не можемо поговорити з сервером про цей ресурс, використовуючи старий ідентифікатор. Я не думаю, що ми можемо уникнути зміни коду в цій ситуації.

Що робити, якщо я хотів, щоб структура URL-адреси веб-програми відрізнялася від структури api?

Ви можете використовувати будь-яку структуру URL-адрес, яку хочете. Зрештою, URL-адреса для закладки для певного ресурсу повинна містити те, що ви можете використовувати для отримання URL-адреси API, яка однозначно ідентифікує цей ресурс. Якщо ви генеруєте власний ідентифікатор і кешуєте його URL-адресою API, як у вашому підході №3, це буде працювати, поки хтось не спробує використати цю закладену URL-адресу після того, як цей запис буде видалено з кеша.

Що робити, якщо суб’єкти мого веб-додатка не відображатимуться до об’єктів api 1-1?

Відповідь залежить від відносин. Так чи інакше, вам знадобиться спосіб зіставлення переднього інтерфейсу до URL-адрес API.


У мене є одна проблема з таким підходом. Це насправді номер 1 у моєму списку рішень. Чого я не отримую, це так: якщо веб-додаток не інтерпретує URL-адреси та розглядає унікальні ідентифікатори як непрозорі рядки (просто person/1, pet/3), то як би він знав, що якщо браузер відкриється, http://my.rest.api/api/person/1він повинен показувати інтерфейс користувача, і якщо він відкриває http://my.rest.api/api/pet/3, то домашній інтерфейс для домашніх тварин
Павло Гатилов

Гарне питання! Я оновив відповідь своєю відповіддю.
Майк Партрідж

Спасибі, Майку. Я думаю, що це вводить надзвичайно щільне з'єднання веб-програми з api. Що робити, якщо я хотів, щоб структура URL-адреси веб-програми відрізнялася від структури api? Що робити, якщо суб’єкти мого веб-додатка не відображатимуться до об’єктів api 1-1? Я все ще думаю, що я краще підходжу до викриття деяких ідентифікаторів, але закликаю клієнтів використовувати посилання для навігації.
Павло Гатилов

Це цікава тема, тож сподіваюся, я нічого не пропускаю. Я оновив свою відповідь відповідями на ваш коментар. Я думаю, що виставлення деяких ідентифікаторів є хорошим компромісом між повною RESTfulness та зручністю використання.
Майк Партрідж

Моя головна турбота тут трохи практичніша. Я використовую ASP.NET MVC для реалізації веб-програми, і через деякі внутрішні правила я повинен визначити шаблони URL-адрес, які підтримує додаток. Тобто, якщо / a / {id} визначено, додаток буде обробляти / a / 1, але не / a / 1 / b / 2. Це призводить до вимоги перекомпілювати веб-додаток, якщо URL-адреси API REST змінюються не лише для збереження URL-адрес із закладеними, але й просто для того, щоб веб-додаток працював під час навігації з кореня. Просто тому, що гіперпосилання, вбудовані в html-сторінки, без цього не працюватимуть.
Павло Гатилов

2

Подивімося, магічного рішення немає. Ви читали модель зрілості Річардсона ? Він розділяє зрілість архітектури REST на 3 рівні: ресурси, дієслова HTTP та елементи контролю гіпермедіа.

Я не повинен виставляти необроблені ідентифікатори моїх сутностей, а повертати гіперпосилання з rel = "self"

Це контролі гіпермедіа. Вам дійсно це потрібно? Цей підхід має деякі дуже хороші переваги (про них ви можете прочитати тут ). Але немає такого поняття, як безкоштовне харчування, і вам доведеться наполегливо працювати (наприклад, ваше друге рішення), якщо ви хочете отримати їх.

Це питання балансу - ви хочете пожертвувати продуктивністю (і зробити свій код складнішим), але отримати більш гнучку систему? Або ви віддаєте перевагу тримати речі швидше і простіше, але платите пізніше, коли вносите зміни до своєї api / моделі?

Як хтось, хто розробив подібну систему (рівень логіки бізнесу, веб-рівень та веб-клієнти), я вибрав другий варіант. Оскільки моя група розробила всі рівні, ми вирішили, що краще трохи зв’язати (повідомивши веб-ярусу про ідентифікатори сутності та створення api-URL-адрес), а взамін отримати простіший код. Зворотна сумісність також не була актуальною в нашому випадку.

Якщо веб-додаток було розроблено третьою стороною або якщо назад була сумісна проблема, ми могли б вибрати інше, тому що тоді велика цінність була можливість змінити структуру URL, не змінюючи веб-додаток. Досить, щоб виправдати ускладнення коду.

Чи все це означає, що API RESTful не можуть служити основою для веб-додатків?

Я думаю, це означає, що вам не потрібно створювати ідеальну REST-реалізацію. Ви можете перейти зі своїм другим рішенням, або викрити ідентифікатори об'єкта або, можливо, передати api-адреси . Добре, поки ви розумієте наслідки та компроміси.


0

Я річ, якщо ти дотримуєшся чогось подібного до Atom Syndication Formatтебе, ти добрий.

Тут метадані, що описують представлений запис, можна вказати за допомогою додаткових елементів / атрибутів:

  • Відповідно до [RFC4287] , містить URI, який однозначно ідентифікує запис

  • Відповідно до [RFC4287] , цей елемент необов’язковий. Якщо він включений, він містить URI, який клієнт повинен використовувати для отримання запису.

Це лише мої два центи.


Можливо, я щось не отримую, але мені здається, що ваша відповідь не пояснює, як генерувати URL-адреси веб-програми, яка є клієнтом API REST, чи не так?
Павло Гатилов

0

Не турбуйтеся про URL-адреси, переживайте про типи медіа.

Дивіться тут (зокрема, третя точка кулі).

API REST повинен витратити майже всі свої описові зусилля для визначення типів мультимедіа, які використовуються для представлення ресурсів і стану керування додатком, або для визначення розширених імен зв’язків та / або розмітки з підтримкою гіпертексту для існуючих стандартних типів медіа. .


У випадку типового веб-додатку клієнт - людина ; браузер - це просто агент .

Тож якорний тег, як

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

відповідає чомусь подібному

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

URL все ще непрозорий для користувача, все, що вона піклується, - це типи медіа (наприклад text/html, application/pdf, application/flv, video/x-flv, image/jpeg, image/funny-cat-picture etc). Описний текст, що міститься в якорі (і в атрибуті заголовка), є лише способом розширення типу відносин таким чином, щоб він був зрозумілий людям.

Причина, за якою ви хочете, щоб URI був непрозорим для клієнтів, полягає в тому, що ви зменшуєте зв'язок (одна з головних цілей REST). Сервер може змінювати / реорганізовувати URI, не впливаючи на клієнта (якщо у вас є хороша політика кешування - це може означати, що кешування взагалі не може бути).

Підсумки

Просто переконайтеся, що клієнт (людина чи машина) піклується про типи медіа та відносини, а не про URL-адреси, і ви будете добре.


Родрік, моє питання не в створенні API, а в тому, щоб створити веб-додаток, який базується на RESTful API. Я навряд чи зрозумію, як типи медіа можуть допомогти мені створити URL-адреси для веб-програми. Хоча типи засобів масової інформації мають вирішальне значення для контракту на послуги та пошуку.
Павло Гатилов

@PavelGatilov - клієнт вашого веб-додатка людина?
Родрік Чапман

Так. І дуже недооцінений.
Павло Гатилов

0

Найпростіший спосіб - це, мабуть, лише використання URL-адрес API як їх рядкових ідентифікаторів. Але веб-сторінки на зразок http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234 некрасиві.

Я думаю, ти маєш рацію, це найпростіший спосіб. Ви можете відновити URL-адреси, http://my.rest.api/apiщоб зробити їх менш некрасивими:

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

Якщо URL-адреса, надана API, не відповідає цій базі, вона погіршується до некрасивої форми:

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

Щоб зробити крок далі, перегляньте відповідь сервера API, щоб визначити, який тип подання ви хочете представити, і перестаньте кодувати розділовувач сегмента шляху та колонки:

http://my.web.app/person/1234 (best case)
http://my.web.app/http://other.api.host/api/person/1234 (ugly case)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.