REST API - должен ли API возвращать вложенные объекты JSON?

38

Когда дело доходит до API JSON, рекомендуется ли выравнивать ответы и избегать вложенных объектов JSON?

В качестве примера, скажем, у нас есть API, похожий на IMDb, но для видеоигр. Существует пара сущностей: Game, Platform, ESRBRating и GamePlatformMap, которые отображают игры и платформы.

Допустим, вы запрашиваете / game / 1, который выбирает игру с идентификатором 1 и возвращает игровой объект с платформами и вложенным esrbRating.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"id":1,"name":"Xbox"},
    {"id":2,"name":"Playstation"}
  ],
  "esrbRating": {
    "id": 1,
    "code": "E",
    "name": "Everyone"
  }
}

Если вы используете что-то вроде JPA / Hibernate, он может автоматически сделать это за вас, если для него установлено значение FETCH.EAGER.

Другой вариант - просто API и добавить больше конечных точек.

В том случае, когда запрашивается / game / 1, возвращается только игровой объект.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
}

Если вам нужны платформы и / или ESRBRating, вам нужно вызвать следующее:

/ игра / 1 / платформа / игра / 1 / esrb

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

У меня была одна последняя мысль, когда ты должен был вернуть что-то подобное.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": ["Xbox","Playstation"]
}

Однако это предполагает, что им не нужны идентификаторы или любая другая информация, которая может быть связана с этими объектами платформы.

Я вообще спросил, как лучше структурировать ваши объекты JSON, возвращаемые из вашего API. Стоит ли вам стараться держаться как можно ближе к своим сущностям, или это нормально - использовать объекты домена или объекты передачи данных? Я понимаю, что методы будут иметь компромисс, либо больше работы на уровне доступа к данным, либо больше работы для клиента.

Я также хотел бы услышать ответ, касающийся использования Spring MVC в качестве базовой технологии для API, с JPA / Hibernate или MyBatis для сохранения.

GreyFox
источник
6
Какие возражения, если таковые имеются, у вас есть возврат вложенных объектов? Возвращение встроенных объектов по отдельности из разных конечных точек будет чертовски раздражающим (не говоря уже о том, что он медленный).
Роберт Харви
1
Лично я не возражаю против этого. Я просто не знаю, что считается лучшими практиками. Коллега утверждает, что работать со встроенными объектами в AngularJS не так просто, и в конечном итоге я хотел бы, чтобы любое приложение Ember of AngularJS использовало API. Я не знаю достаточно об Angular или Ember, чтобы знать, окажет ли это влияние или нет.
Greyfox
3
Ответ будет зависеть от того, хотите ли вы вернуть доменные объекты, DTO, объекты ViewModel или KitchenSink. Какой объект вы возвращаете, скорее всего, будет зависеть от того, что нужно вашему приложению, и от того, как этот объект ведет себя через Интернет. Пример: если вы пытаетесь заполнить веб-страницу данными из счета-фактуры, очень вероятно, что вы собираетесь вернуть объект, содержащий все, что вам нужно (если вы не планируете AJAXing в позициях или что-то в этом роде).
Роберт Харви
Это тот случай, когда вы запрашиваете игру, вы, вероятно, захотите узнать жанры, платформы и ESRBRating. В этом есть смысл. С точки зрения дизайна с точки зрения Java, вы бы порекомендовали иметь пакет Entity с JPA-объектами, а затем пакет домена, который представляет собой возвращаемые пользователю бизнес-объекты / DTO?
Greyfox
1
Звонки на сервер стоят дорого. API, который требует от вас отправки данных с использованием нескольких вызовов, будет медленнее, чем API, который позволяет вам получить все за один вызов, часто даже когда последний возвращает ненужную информацию.
Gort the Robot

Ответы:

11

Другая альтернатива (используя HATEOAS). Это просто, в основном на практике вы добавляете тег ссылок в json в зависимости от того, как вы используете HATEOAS.

http://api.example.com/games/1:

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"_self": "http://api.example.com/games/1/platforms/53", "name": "Playstation"},
    {"_self": "http://api.example.com/games/1/platforms/34", "name": "Xbox"},
  ]
}

http://api.example.com/games/1/platforms/34:

{
  "id": 34,
  "title": "Xbox",
  "publisher": "Microsoft",
  "releaseDate": "2015-01-01",
  "testReport": "http://api.example.com/games/1/platforms/34/reports/84848.pdf",
  "forms": [
    {"type": "edit", "fields: [] },
  ]
}

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

Техническая реализация может содержать кеширование. Вы можете кэшировать ссылки и названия платформ в игровом объекте и отправлять их мгновенно, не загружая API платформ вообще. Затем при необходимости вы можете загрузить его.

Вы видите, например, что я добавил некоторую информацию о форме. Я сделал это, чтобы показать вам, что в подробном объекте json может быть гораздо больше информации, чем вы бы хотели загрузить в список игр.

Люк Франкен
источник
Я не думаю, что это технически HATEOS, так как нет государства.
Рибальд Эдди
Да, не уверен, точное слово об этом процессе. HATEOS в целом используется для ссылок в остальных API, но я согласен, что это также связано с состоянием. Хотя идея реализации будет той же. Здесь вы увидите немного больше о том, как это можно использовать на примере: stormpath.com/blog/linking-and-resource-expansion-rest-api-tips
Люк Франкен,
Это хорошая идея, хотя!
Рибальд Эдди
1
Если вы разрабатываете API, в котором существует согласованность между клиентом и самим API (скажем, внутренним API), возможно, имеет смысл возвращать вложенный (или плоский) ответ, а не предоставлять ссылки на другой ресурс, что означает больше запросов API что может быть нежелательным.
Бруно
@bruno да, но с ограничением: в больших системах вы не можете или не хотите предоставлять все связанные объекты в полном объеме. Поля, которые вы включаете по умолчанию, являются произвольными, вы можете выбрать их в зависимости от использования вашего API. Так что в этом случае у вас могут быть платформы с сотнями полей, в сценарии использования для выбора платформы выбирается окно выбора. Тогда имеет смысл включить название платформы, но для этого не нужны, например, финансовые подробности платформы.
Люк Франкен
16

Это один из основных вопросов, касающихся дизайна REST API. Каждый дизайнер задает себе этот вопрос в первый день. Извините, но ответ "это зависит". У каждого подхода есть свои плюсы и минусы, и вам просто нужно принять решение и пойти с ним.

RibaldEddie
источник
11
Это совсем не полезно. Сам ОП знал, что «это зависит, и у каждого подхода есть свои плюсы и минусы». Вы должны объяснить, от чего это зависит, или, по крайней мере, привести пример.
Пратик Сингхал
5

Я второй подход, представленный здесь https://www.slideshare.net/stormpath/rest-jsonapis

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

На мой взгляд, это эффективный и гибкий способ в большинстве случаев.

Вэй Цю
источник
2
Мне нравится этот подход. Для всех, кто интересуется, это начинается на слайде 57 в связанном слайд-шоу.
Адам Плохер