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 для сохранения.


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


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

5

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

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

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


2
Мне нравится этот подход. Для всех, кто интересуется, это начинается на слайде 57 в связанном слайд-шоу.
— Адам Плохер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.