Каков наилучший способ вернуть массив в качестве ответа в RESTful API?

41

Предположим, у нас есть такие ресурсы,

book:
    type: object
    properties:
        author: {type: string}
        isbn: {type: string}
        title: {type: string}

books:
    type: array
    items: book

Таким образом, когда кто-то делает GETна ресурсе книги, мы будем возвращать следующее

[{"author": "Dan Brown", "isbn": "123456", "title": "Digital Fortress"},
 {"author": "JK Rowling", "isbn": "234567", "title": "Harry Potter and the Chamber of Secrets"}]

Я слышал от кого-то на работе, что рекомендуемая практика REST - всегда возвращать ответы в виде объектов JSON, что будет означать, что наша схема booksбудет выглядеть так,

books:
    type: object
    properties:
        list:
            type: array
            items: book

Итак, теперь ответ будет выглядеть так,

{
    "list": [{"author": "Dan Brown", "isbn": "123456", "title": "Digital Fortress"},
             {"author": "JK Rowling", "isbn": "234567", "title": "Harry Potter and the Chamber of Secrets"}]
}

Какой из этих методов лучше всего подходит для отдыха?

borncrusader
источник
1
JSON RESTful? Вы должны вернуть HTML наверняка?
Эван
3
@Ewan: полезная нагрузка не имеет значения. Вот для чего нужны MIME-типы.
Роберт Харви
1
Ни одна из лучших практик для ОТДЫХА. REST сделан из HATEOAS, что означает обнаружение вашего API. Посмотрите на HAL или JSON-LD.
Флориан Маргейн
Json-ld: медленно продвигаясь к WCF
Юань
Из того, что я прочитал, обертывающие массивы JSON внутри объекта являются защитной мерой против обнаруженной уязвимости в старых браузерах - haacked.com/archive/2009/06/25/json-hijacking.aspx . Это, кажется, было исправлено в современных браузерах сегодня. Лучше быть в безопасности, чем
Гишу

Ответы:

35

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

Например: если вам нужно добавить количество всех записей, вы уже сделали с использованием только массива.

Если это происходит в одном API-интерфейсе списка, то вы хотите, чтобы он был единообразным, поэтому сделайте все объекты, тогда ваш API-интерфейс станет более согласованным и более простым в использовании для разработчиков.

Например: допустим, разработчик пишет общий код, чтобы использовать ваш API для отображения списка и подробных страниц. Он не хочет создавать исключение, потому что иногда это массив, а иногда объект со свойством списка.

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

Люк Франкен
источник
3
+1 за то, что «реальный о данных» (и все же признавая, что есть более техническое, более точное определение для REST).
Тридцать
9

И то и другое

[{"author": "Dan Brown", "isbn": "123456", "title": "Digital Fortress"},{"author": "JK Rowling", "isbn": "234567", "title": "Harry Potter and the Chamber of Secrets"}]

а также

{
    "list": [{"author": "Dan Brown", "isbn": "123456", "title": "Digital Fortress"},
         {"author": "JK Rowling", "isbn": "234567", "title": "Harry Potter and the Chamber of Secrets"}]
}

действительны Json. Я не думаю, что вы должны добавлять «список», если он не нужен, это может даже сбить с толку, потому что за ним следует массив, а не список.

Лучшая практика REST? API должен дать правильный ответ на любой набор в заголовке Accept, а также хорошую документацию.

imel96
источник
7

Причина, по которой вы делаете свой ответ JSON-совместимым, заключается в том, что JSON является стандартом де-факто; любой язык с анализатором JSON может его тривиально проанализировать, и если вы используете JavaScript, вам даже не нужен анализатор, поскольку JavaScript понимает его изначально.

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

REST не имеет ничего общего с вашей схемой JSON. Любая схема является приемлемой с точки зрения REST.

Роберт Харви
источник
9
Это отвечает на вопрос? Я читаю это как «Должен ли я использовать массив json или объект json как root?». Оба могут быть проанализированы с помощью парсеров json, поэтому ваш ответ не поможет им принять решение.
CodesInChaos
Тогда это не имеет значения. Я обновил свой ответ.
Роберт Харви
Если мы говорим о REST, схема не имеет значения, если она способна обеспечить элементы управления гипермедиа для обнаружения и манипулирования другими ресурсами на основе одного ответа и никакой другой внеполосной информации ... которая похоже, ни один из форматов, упомянутых в ОП, не подходит.
toniedzwiedz
...and if you're using JavaScript, you don't even need a parser since JavaScript understands it natively.Ну да и нет. JSON является подмножеством JavaScript, но вызов evalвместо использования парсера сразу делает вас уязвимым для «JSON», содержащего вредоносный код, и синтаксический анализ, скорее всего, гораздо эффективнее, чем в evalлюбом случае.
Довал
5

Словарь с единственным бессмысленным ключом «список» и значением массива не имеет смысла - вместо этого возвращайте массив.

Если один и тот же сервис может возвращать книги, компакт-диски или DVD-диски, то вы можете вернуть словарь с ключом «книги» и значением массива. Может быть другой ключ «DVD» с массивом DVD. Например, если клиент может запросить список всех своих покупок.

Если вы уверены, что ответ будет интерпретирован только как список книг (если в запросе указано «дайте мне список книг»), то подойдет только массив.

gnasher729
источник
5

Второй вариант также является предпочтительным методом по соображениям безопасности. В старых браузерах есть уязвимость, которая позволяет другому коду javascript на веб-странице украсть ваши данные, если они возвращаются в виде массива JSON. Поэтому исторически лучше всего не возвращать массивы JSON. Фактически, были некоторые фреймворки, чья функция "json-ify" выбирает вариант 2 по умолчанию, когда вы передаете массив.

https://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk

http://ejohn.org/blog/re-securing-json/

цыпленок
источник
1

оба являются JSON и придерживаются REST. Я бы сделал ответ более информативным, в вашем случае измените список на книги. Или как то так:

{ "responceObject" : {

   results : 2,

    "Books": [
        {"author": "Dan Brown", "isbn": "123456", "title": "Digital Fortress"},
        {"author": "JK Rowling", "isbn": "234567", "title": "Harry Potter and the Chamber of Secrets"}
    ]

}}
MeganFoxObama
источник