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

162

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

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

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

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

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

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

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

  • 0
    имеет ли смысл использовать RESTFUL SERVICE DISCOVERY ?
  • 1
    Я пытаюсь придерживаться HATEAOS, что, на мой взгляд, противоречит использованию чего-то подобного, но я посмотрю.
Показать ещё 1 комментарий
Теги:
resources
rest
design
nested-resources

2 ответа

58

@ray, отличное обсуждение

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

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

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

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

Однако, если вы рассматриваете сообщение электронной почты как ресурс и адрес от него как дочерний ресурс, вы, очевидно, все равно можете ссылаться на его адрес отдельно. Например, получите все адреса. Или создайте новое сообщение с предыдущим адресом. Если email был REST, вы можете легко увидеть, что могут быть доступны многие ресурсы с перекрестными ссылками:/полученные сообщения,/черновики-сообщения,/from-addresses,/to-addresses,/addresses,/themes,/attachments,/folders,/tags,/categories,/labels и др.

В этом учебном пособии представлен отличный пример перекрестных ссылок. 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 или подобных.

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

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

GET/комиксы
= > 200 OK, Получите все комиксы.

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

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

GET/охватывает
= > 200 OK, получите все покрытия.

GET/covers/1
= > 200 OK, Получить обложку (id: 1) с помощью комикса (/комиксы/0).

GET/комиксы/новые
= > 200 OK, Получить форму для создания комикса (форма: POST/черновик-комиксы).

POST/черновик комиксов
название = Foo
автор = бух
издатель = липкая
опубликовано = 2011-01-01
= > 302 Найдено, Расположение:/draft-comic-books/3, Перенаправление в черновик комикса (id: 3) с обложками (двоичный).

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

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

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

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

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

POST/опубликованные комиксы
название = Foo
автор = бух
издатель = липкая
опубликовано = 2011-01-01
cover_type = передняя
cover_data = (двоичный)
= > 302 Найдено, Расположение:/комиксы/3, Перенаправление на опубликованный комикс (id: 3) с обложками.

  • 0
    Я абсолютный новичок в этом и пытаюсь выучить это в спешке. Я нашел это чрезвычайно полезным. Тем не менее, в других блогах и т. Д., Которые я читал сегодня, использование GET для выполнения операции (особенно операции, которая не является идемпотентной) будет осуждено. Так не должно ли это быть POST / draft-comic-books / 3 / publish?
  • 3
    @GaryMcGill В своем примере / draft-comic-books / 3 / publish возвращает только HTML-форму (не изменяет никаких данных).
Показать ещё 10 комментариев
39

Обработка покрытий в качестве ресурсов определенно в духе 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

с обложкой в ​​теле объекта.

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

  • Сначала создайте обложку, теперь по существу делаете покрытие независящим ресурсом, или вы помещаете начальную обложку в тело объекта POST, который создает комикс. Это, как вы говорите, означает, что представление, которое вы создаете POST, будет отличаться от представления, которое вы GET.

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

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

Какой из трех вариантов вы должны принять? Не зная слишком много о вашей ситуации, но ответьте на общий вопрос 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 "быть одинаковыми", но вы просто не хотите использовать "обложку" в GET и не закрывать POST. Надеюсь, что это имеет смысл.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню