REST Комплекс / Композитні / Вкладені ресурси [закрито]


177

Я намагаюся обернути голову навколо найкращого способу вирішення понять в API, заснованому на REST. Плоскі ресурси, які не містять інших ресурсів, не є проблемою. Там, де я стикаюся з проблемою, є складні ресурси.

Наприклад, у мене є ресурс для коміксу. ComicBookмає всі види властивостей на нього , як author, issue number, dateі т.д.

У коміксі також є список 1..nобкладинок. Ці обкладинки є складними об’єктами. Вони містять багато інформації про обкладинку: про виконавця, дату та навіть базове 64 закодоване зображення обкладинки.

Для того, GETщоб ComicBookя просто міг повернути комічний, і всі обкладинки, включаючи їх базові64'еди. Мабуть, це не велика справа для отримання єдиного коміксу. Але припустимо, я будую клієнтський додаток, який хоче перелічити всі таблиці коміксів у системі в таблиці.
Таблиця міститиме декілька властивостей ComicBookресурсу, але ми точно не хочемо відображати всі обкладинки таблиці. Повертаючись до 1000 коміксів, кожен з декількома обкладинками призведе до смішно великої кількості даних, що надходять через провід, даних, які не потрібні кінцевому користувачеві в такому випадку.

Мій інстинкт - створити Coverресурс і мати ComicBookобкладинки. Отже, зараз Coverє URI. GETна коміксах працює зараз, замість величезного Coverресурсу ми надсилаємо назад URI для кожної обкладинки, і клієнти можуть отримати ресурси Cover так, як вони цього вимагають.

Зараз у мене проблема зі створенням нових коміксів. Безумовно, я хочу створити хоча б одну обкладинку, коли створюю Comic, адже це, мабуть, ділове правило.
Так що тепер я застряг, я або змусити клієнт для забезпечення дотримання бізнес - правил по першій подачі Cover, отримуючи URI для цієї обкладинки, то POSTІнги ComicBookз цим URI в списку, або мій POSTна ComicBookбере в іншому шукають ресурс , ніж він випльовує з. Вхідні ресурси для POSTта GETє копіями, де вихідні GETмістять посилання на залежні ресурси.

CoverРесурс, ймовірно , необхідний в будь-якому випадку , тому що я впевнений , що в якості клієнта я хочу напрямок чохлів адреси в деяких випадках. Тож проблема існує в загальній формі незалежно від розміру залежного ресурсу. Загалом, як ви обробляєте складні ресурси, не змушуючи клієнта просто «знати», як ці ресурси складаються?


чи має сенс використання RESTFUL SERVICE DISCOVERY ?
treecoder

1
Я намагаюся дотримуватися HATEAOS, який, на мій погляд, суперечить використанню чогось подібного, але я погляну.
jgerman

Різне питання в тому ж дусі. Однак право власності відрізняється від запропонованого Вами рішення (те, що в питанні). stackoverflow.com/questions/20951419/…
Уес

Відповіді:


64

@ray, відмінна дискусія

@jgerman, не забувайте, що те, що це REST, не означає, що ресурси повинні бути встановлені в камені від POST.

Що ви вирішите включити в будь-яке подання ресурсу, залежить від вас.

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

Ваш конкретний випадок обкладинок трохи складніший, оскільки обкладинка дійсно вимагає коміксу, і навпаки.

Однак якщо ви розглядаєте повідомлення електронної пошти як ресурс, а адресу - як дочірній ресурс, очевидно, ви все одно можете посилатися на адресу окремо. Наприклад, отримати все з адрес. Або створити нове повідомлення з попереднім адресою. Якщо електронна пошта була REST, ви можете легко побачити, що багато перехресних посилань можуть бути доступні: / отримані повідомлення, / чернетні повідомлення, / з адрес, / на адреси, / адреси, / теми, / вкладення, / папки , / теги, / категорії, / мітки та ін.

Цей підручник дає чудовий приклад перехресних посилань на ресурси. http://www.peej.co.uk/articles/restrely-delicious.html

Це найпоширеніший шаблон для автоматично генерованих даних. Наприклад, ви не публікуєте URI, ID або дату створення нового ресурсу, оскільки вони генеруються сервером. І все ж ви можете отримати URI, ідентифікатор або дату створення, коли повернете новий ресурс.

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

Форми та параметри вже відрізняються, ніж HTML-представлення ресурсів. Опублікування бінарного / параметра параметра, що призводить до отримання URL-адреси, не є розтяжкою.

Коли ви отримуєте форму для нового ресурсу (/ коміксів / новий) або отримуєте форму для редагування ресурсу (/ коміксів / 0 / редагування), ви запитуєте представлення ресурсу для конкретних форм. Якщо ви розмістите його в колекції ресурсів із вмістом типу "application / x-www-form-urlencoded" або "multipart / form-data", ви просите сервер зберегти представлення цього типу. Сервер може відповісти збереженим представленням HTML або будь-яким іншим.

Ви також можете дозволити представлення HTML, XML або JSON в колекцію ресурсів для цілей API чи подібних.

Можна також представляти свої ресурси та робочий процес так, як ви описуєте, враховуючи обкладинки, розміщені після коміксу, але вимагаючи, щоб комікси мали обкладинку. Приклад наступний.

  • Дозволяє затримувати створення обкладинки
  • Дозволяє створювати комікси з необхідною обкладинкою
  • Дозволяє перехресне посилання на обкладинки
  • Дозволяє декілька обкладинок
  • Створіть проект коміксу
  • Створіть обкладинки проектів коміксів
  • Опублікувати проект коміксу

GET / comic-books
=> 200 ОК, отримуйте всі комікси.

GET / comic-books / 0
=> 200 ОК, дістаньте комікс (id: 0) із обкладинками (/ обкладинки / 1, / обкладинки / 2).

GET / comic-books / 0 / обкладинки
=> 200 ОК, отримайте обкладинки для коміксів (id: 0).

GET / cover
=> 200 ОК, дістаньте всі обкладинки.

GET / обкладинки / 1
=> 200 ОК, дістаньте обкладинку (id: 1) із коміксів (/ коміксів / 0).

GET / comic-books / new
=> 200 ОК, Отримайте форму для створення коміксу (форма: POST / чернетка-комікс).

POST / draft-comic-books
title = foo
author = boo
publisher = goo
published = 2011-01-01
=> 302 Знайдено, Розташування: / draft-comic-books / 3, Перенаправлення на проект коміксу (id: 3) з обкладинки (двійкові).

GET / чернетки-комікси / 3
=> 200 ОК, дістаньте проект коміксу (id: 3) із обкладинками.

GET / чернетки-комікси / 3 / обкладинки
=> 200 ОК, отримати обкладинки для проекту коміксів (/ чернетка-комікс / 3).

GET / чернетки-комікси / 3 / обкладинки / нові
=> 200 ОК, Отримайте форму для створення обкладинки для проекту коміксу (/ чернетка-комікс / 3) (форма: POST / чернетка-комікс / 3 / обкладинки).

POST / чернетки-комікси / 3 / обкладинки
cover_type = передня
обкладинка_дані = (двійкові)
=> 302 Знайдено, Розташування: / чернетки-комікси / 3 / обкладинки, Перенаправлення на нову обкладинку для проекту коміксу (/ проект-комікс -книга / 3 / обкладинки / 1).

GET / чернетки-комікси / 3 / публікація
=> 200 ОК, Отримайте форму для публікації проекту коміксів (id: 3) (форма: POST / published-comic-books).

POST / published-comic-books
title = foo
author = boo
publisher = goo
published = 2011-01-01
cover_type = front
cover_data = (binary)
=> 302 Знайдено, Розташування: / comic-books / 3, Перенаправлення на опублікований комікс (id: 3) з обкладинками.


Я абсолютно новачок у цьому, і намагаюся навчитися цьому поспішати. Я вважав це надзвичайно корисним. Однак, в інших блогах тощо, які я читав сьогодні, використання GET для здійснення операції (особливо операції, яка не є безсильною) було б насуплено. Так чи не має бути POST / чернетки-комікси / 3 / публікація?
Гері Макгілл

3
@GaryMcGill У своєму прикладі / чернетки-комікси / 3 / публікація повертає лише HTML-форму (не змінює жодних даних).
Олів'є Лалонде

@Olivier правильний. Слово опублікувати є для позначення того, що робить форма. Однак, оскільки ви хочете, щоб дієслова обмежилися методами HTTP, вам слід відправляти повідомлення на ресурс для опублікованих коміксів. ... Якщо це веб-сайт, вам може знадобитися URI для форми, щоб щось опублікувати. ... Хоча, якщо дія над публікацією була лише однією кнопкою на сторінці коміксів, ця форма однієї кнопки могла б розміщуватись безпосередньо в URI / опублікованих коміксів.
Олексій

@Alex, у запиті POST я б замість цього повернув 201 Створений з URL-адресою нового ресурсу як Місце розташування у заголовках відповідей.
ismriv

2
@Stephane, переспрямовування просто робить все простішим для контролерів. Навіть для API, простіше, якщо контролер створення повертає місце для нового вмісту, а потім дозволити контролеру показу обробляти відображення нового вмісту. Хоча, клієнту API краще / простіше просто отримувати вміст, а не перейматися переадресаціями.
Алекс

45

Трактування покривів як ресурсів, безумовно, відповідає духу REST, особливо HATEOAS. Так, так, GETзапит http://example.com/comic-books/1надати вам уявлення про книгу 1 із властивостями, що включає набір URI для обкладинок. Все йде нормально.

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

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

Дані коміксів без обкладинки створять новий комікс і повернуть створений на сервері ідентифікатор (скажімо, він повертається як 8), і тепер ви можете додавати обкладинки до нього так:

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

з покриттям в тілі органу.

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

  1. Спершу примушуйте створювати обкладинку, тепер по суті роблячи обкладинку не залежною від ресурсу, або ви розміщуєте початкову обкладинку в об'єкті POST, який створює комікс. Це, як ви кажете, означає, що представлення, яке ви POST створюєте, буде відрізнятися від представлення, яке ви отримуєте.

  2. Визначте поняття первинного, початкового, або бажаного, або позначеного іншим чином обкладинки. Ймовірно, це злом моделювання, і якщо ви це зробили, це було б як налаштування вашої об'єктної моделі (вашої концептуальної чи ділової моделі), щоб відповідати технології. Не чудова ідея.

Ви повинні зважити ці два варіанти проти простого дозволу коміксів без покриттів.

Який із трьох варіантів слід зробити? Не знаючи занадто багато про вашу ситуацію, але відповідаю на загальне питання, що залежить від ресурсів, я б сказав:

  • Якщо ви можете піти з 0..N для вашого рівня обслуговування RESTful, чудово. Можливо, шар між вашим RESTful SOA може впоратися з подальшим обмеженням у бізнесі, якщо потрібно хоча б одне. (Не впевнений, як це виглядатиме, але, можливо, варто вивчити .... кінцеві користувачі зазвичай не бачать SOA.)

  • Якщо ви просто повинні моделювати обмеження на 1..N, тоді запитайте себе, чи обкладинки можуть бути просто спільними ресурсами, іншими словами, вони можуть існувати на речах, окрім коміксів. Тепер вони не є залежними ресурсами, і ви можете створити їх першими і поставити URI у своєму POST, який створює комікси.

  • Якщо вам потрібно 1..N і обкладинки залишаються залежними, просто розслабте свій інстинкт, щоб представлення в POST і GET були однаковими, або зробіть їх однаковими.

Останній пункт пояснюється так:

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

Коли ви надсилаєте пошту, ви дозволяєте наявні урири, якщо у вас є (запозичені з інших книг), а також розміщені в одному або декількох початкових зображеннях. Якщо ви створюєте книгу, і у вашої особи немає початкового зображення обкладинки, поверніть відповідь 409 або подібну. У GET ви можете повернути URI.

Таким чином, ви дозволяєте представленням POST та GET "бути однаковими", але ви просто вирішите не "використовувати" зображення обкладинки на GET, ані обкладинки на POST. Сподіваюся, що це має сенс.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.