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

177

Я пытаюсь обдумать лучший способ решения концепций в API на основе REST. Плоские ресурсы, которые не содержат других ресурсов, не являются проблемой. Где я сталкиваюсь с неприятностями, это сложные ресурсы.

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

В комиксе также есть список 1..nобложек. Эти обложки являются сложными объектами. Они содержат много информации об обложке: артист, дату и даже 64-битное изображение обложки.

Для GETна ComicBookя мог бы просто вернуться комическое, и все обложки , включая их base64'ed изображений. Это, вероятно, не имеет большого значения для получения одного комикса. Но предположим, что я создаю клиентское приложение, которое хочет перечислить все комиксы в системе в виде таблицы.
Таблица будет содержать несколько свойств ComicBookресурса, но мы, конечно, не собираемся отображать все обложки в таблице. Возвращение 1000 комиксов, каждая с несколькими обложками, привело бы к смехотворно большому количеству данных, поступающих по проводам, данных, которые в этом случае не нужны конечному пользователю.

Мой инстинкт состоит в том, чтобы Coverсоздать ресурс и ComicBookсодержать обложки. Так что теперь Coverэто URI. GETв комиксах теперь работает, вместо огромного Coverресурса мы отправляем обратно URI для каждой обложки, и клиенты могут извлекать ресурсы обложки по мере необходимости.

Теперь у меня проблема с созданием новых комиксов. Конечно, я хочу создать хотя бы одну обложку, когда создаю Comic, на самом деле это, вероятно, бизнес-правило.
Так что теперь я застрял, я либо заставляю клиентов применять бизнес-правила, сначала отправив a Cover, получив URI для этого покрытия, затем POSTвставив ComicBookс этим URI в список, либо мой POSTon ComicBookполучает другой, выглядящий ресурс, чем он плюет вне. Входящие ресурсы для POSTи GETявляются глубокими копиями, где исходящие GETсодержат ссылки на зависимые ресурсы.

CoverРесурс, вероятно , необходим в любом случае , потому что я уверен , что в качестве клиента я хочу направление чехлов адреса в некоторых случаях. Таким образом, проблема существует в общем виде независимо от размера зависимого ресурса. В целом, как вы обрабатываете сложные ресурсы, не заставляя клиента просто «знать», как эти ресурсы составлены?

jgerman
источник
имеет ли смысл использовать RESTFUL SERVICE DISCOVERY ?
древовидный код
1
Я пытаюсь придерживаться HATEAOS, что, на мой взгляд, противоречит использованию чего-то подобного, но я посмотрю.
Jgerman
Другой вопрос в том же духе. Однако право собственности отличается от предложенного вами решения (тот, который в вопросе). stackoverflow.com/questions/20951419/…
Уэс

Ответы:

64

@ray, отличная дискуссия

@jgerman, не забывайте, что только потому, что это REST, не означает, что ресурсы должны быть установлены в камне из POST.

То, что вы решите включить в любое представление ресурса, зависит от вас.

Ваш случай обложек, на которые ссылаются отдельно, является просто созданием родительского ресурса (комикса), чьи дочерние ресурсы (обложки) могут иметь перекрестные ссылки. Например, вы также можете указать ссылки на авторов, издателей, персонажей или категории по отдельности. Вы можете создать эти ресурсы отдельно или перед комиксами, которые ссылаются на них как на дочерние ресурсы. В качестве альтернативы вы можете создать новые дочерние ресурсы при создании родительского ресурса.

Ваш конкретный случай обложек немного сложнее, поскольку обложка действительно требует комиксов, и наоборот.

Однако, если вы рассматриваете сообщение электронной почты как ресурс, а адрес отправителя - как дочерний ресурс, очевидно, что вы все равно можете ссылаться на адрес отправителя отдельно. Например, получить все по адресам. Или создайте новое сообщение с предыдущим адресом от. Если бы электронная почта была REST, вы могли легко увидеть, что многие перекрестные ссылки могут быть доступны: / полученные-сообщения, / черновые сообщения, / от-адреса, / на адреса, / адреса, / темы, / вложения, / папки , / теги, / категории, / ярлыки, и др.

Этот учебник представляет собой отличный пример перекрестных ссылок ресурсов. http://www.peej.co.uk/articles/restfully-delicious.html

Это наиболее распространенный шаблон для автоматически генерируемых данных. Например, вы не публикуете URI, ID или дату создания для нового ресурса, так как они генерируются сервером. И все же вы можете получить URI, ID или дату создания, когда получите новый ресурс обратно.

Пример в вашем случае двоичных данных. Например, вы хотите опубликовать двоичные данные как дочерние ресурсы. Когда вы получите родительский ресурс, вы можете представить эти дочерние ресурсы как те же двоичные данные или как URI, которые представляют двоичные данные.

Формы и параметры уже отличаются от представлений ресурсов в HTML. Публикация бинарного / файлового параметра, который приводит к URL, не растягивается.

Когда вы получаете форму для нового ресурса (/ comic-books / new) или получаете форму для редактирования ресурса (/ comic-books / 0 / edit), вы запрашиваете специфичное для формы представление ресурса. Если вы публикуете его в коллекции ресурсов с типом контента «application / x-www-form-urlencoded» или «multipart / form-data», вы просите сервер сохранить это представление типа. Сервер может ответить сохраненным представлением HTML или чем-то еще.

Вы также можете разрешить размещение представления HTML, XML или JSON в коллекции ресурсов для целей API или аналогичного.

Вы также можете представить свои ресурсы и рабочий процесс так, как вы их описали, принимая во внимание обложки, опубликованные после комиксов, но требующие обложек комиксов. Пример следующим образом.

  • Позволяет отложить создание обложки
  • Позволяет создавать комиксы с необходимой обложкой
  • Позволяет перекрестные ссылки на обложки
  • Позволяет несколько обложек
  • Создать черновик комиксов
  • Создавать черновики обложек комиксов
  • Опубликовать черновик комиксов

ПОЛУЧИТЬ / комиксы
=> 200 ОК, получить все комиксы.

GET / comic-books / 0
=> 200 OK, получить комикс (id: 0) с обложками (/ cover / 1, / cover / 2).

GET / comic-books / 0 / cover
=> 200 OK, Получить обложки для комиксов (id: 0).

ПОЛУЧИТЬ / обложки
=> 200 ОК, получить все обложки.

GET / cover / 1
=> 200 OK, Получить обложку (id: 1) с комиксами (/ comic-books / 0).

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

POST / draft-comic-books
title = foo
author = boo
publisher = goo
опубликовано = 2011-01-01
=> 302 Найдено, Местоположение: / draft-comic-books / 3, Перенаправить на черновик комиксов (id: 3) с обложки (двоичные).

GET / draft-comic-books / 3
=> 200 OK, получите черновик комиксов (id: 3) с обложками.

GET / draft-comic-books / 3 / cover
=> 200 OK, Получить обложки для черновика комиксов (/ draft-comic-book / 3).

GET / draft-comic-books / 3 / cover / new
=> 200 ОК, Получить форму для создания обложки для черновика комиксов (/ draft-comic-book / 3) (форма: POST / draft-comic-books / 3 / крышки).

POST / draft-comic-books / 3 / cover
cover_type = front
cover_data = (binary)
=> 302 Найдено, Местоположение: / draft-comic-books / 3 / cover, Перенаправить на новую обложку для черновика комиксов (/ draft-comic -book / 3 / обложки / 1).

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

POST / опубликованные комиксы
title = foo
author = boo
publisher = goo
опубликовано = 2011-01-01
cover_type = front
cover_data = (двоичный файл)
=> 302 найдено, расположение: / comic-books / 3, перенаправить на опубликованный комикс (id: 3) с крышками.

Alex
источник
Я абсолютный новичок в этом и пытаюсь выучить это в спешке. Я нашел это чрезвычайно полезным. Тем не менее, в других блогах и т. Д., Которые я читал сегодня, использование GET для выполнения операции (в частности, операции, которая не является идемпотентной) будет осуждено. Так не должно ли это быть POST / draft-comic-books / 3 / publish?
Гэри МакГилл
3
@GaryMcGill В своем примере / draft-comic-books / 3 / publish возвращает только HTML-форму (не изменяет никаких данных).
Оливье Лалонд
@ Оливье правильно. Слово «публиковать» используется для обозначения того, что делает форма. Однако, поскольку вы хотите, чтобы глаголы были ограничены методами HTTP, вы должны публиковать материалы для опубликованных комиксов. ... Если бы это был веб-сайт, вам может понадобиться URI для формы, чтобы что-то опубликовать. ... Хотя, если действие публикации было просто нажатием одной кнопки на странице комиксов, эта однокнопочная форма могла бы публиковаться непосредственно в URI / Опубликованные комиксы.
Алекс
@Alex, в запросе POST я бы вместо этого возвратил 201 Created с URL нового ресурса как Location в заголовках ответа.
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. Определите понятие первичного, или начального, или предпочтительного, или иного обозначенного покрытия. Вероятно, это хак для моделирования, и если вы это сделаете, это будет похоже на настройку вашей объектной модели (вашей концептуальной или бизнес-модели), чтобы она соответствовала технологии. Не отличная идея.

Вы должны взвесить эти два варианта против простого разрешения комиксов без обложки.

Какой из трех вариантов вы должны выбрать? Не зная слишком много о вашей ситуации, но отвечая на общий вопрос 1..N зависимого ресурса, я бы сказал:

  • Если вы можете использовать 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>

Когда вы отправляете POST, вы разрешаете существующие Uris, если они у вас есть (позаимствованы из других книг), но также добавляете одно или несколько исходных изображений. Если вы создаете книгу, а ваша сущность не имеет начального обложки, верните 409 или аналогичный ответ. На GET вы можете вернуть URI.

Таким образом, в основном вы позволяете представлениям POST и GET «быть одинаковыми», но вы просто выбираете не «использовать» cover-image в GET и не использовать cover в POST. Надеюсь, что это имеет смысл.

Рэй Тоал
источник