Стандартный формат ответа JSON API?

700

Существуют ли стандарты или лучшие практики для структурирования ответов JSON из API? Очевидно, что данные каждого приложения различны, так что меня это не касается, а скорее «шаблон ответа», если хотите. Пример того, что я имею в виду:

Успешный запрос:

{
  "success": true,
  "payload": {
    /* Application-specific data would go here. */
  }
}

Неудачный запрос:

{
  "success": false,
  "payload": {
    /* Application-specific data would go here. */
  },
  "error": {
    "code": 123,
    "message": "An error occurred!"
  }
}
FtDRbwLXw6
источник
16
Люди, вероятно, извлекли уроки из SOAP и не будут строить его снова ...
Денис Сегюре
18
@dystroy: Хотите объяснить свой комментарий?
FtDRbwLXw6
5
Этот вопрос меня очень заинтересовал, так как недавно мне пришлось разрабатывать JSON API, и я подумал, что это какие-то стандарты, определяющие формат ответа. Ваш на самом деле выглядит довольно хорошо, и выглядит полезным, если вы не найдете стандарт. Обидно, что предоставленные ответы на самом деле не касаются вопроса.
Алекс
13
@ Алекс, к сожалению, это потому, что независимо от того, куда вы идете, нет никакого стандарта. Не только внутри самого JSON, но и с точки зрения того, как использовать его для приложений RESTful или чего-либо еще в этом роде. Все делают это по-разному. Вы можете свободно следовать рекомендациям (HTTP-ответы, содержательная структура пакета, взгляд на структурирование ваших данных для потребления вашей системой), но каждый, кто является крупным дистрибьютором, делает по крайней мере одну вещь, отличающуюся от других. .. Нет стандарта, и, скорее всего, его не будет, поэтому постройте что-нибудь солидное и сделайте так, чтобы оно вам подходило.
Норгард
5
@Norguard есть стандарты (см. Мой ответ). На самом деле, хорошая вещь о стандартах в том, что у вас есть так много выбора. - Эндрю Таненбаум
Адам Гент

Ответы:

643

Да, есть пара стандартов (хотя и с определенными свободами в определении стандарта):

  1. JSON API - JSON API охватывает также создание и обновление ресурсов, а не только ответы.
  2. JSend - Простой и, вероятно, то, что вы уже делаете.
  3. OData JSON Protocol - очень сложный.
  4. HAL - как OData, но стремится быть как HATEOAS .

Есть также форматы описания JSON API:

  • развязность
    • Схема JSON (используется Swagger, но вы можете использовать его отдельно)
  • WADL в JSON
  • RAML
  • HAL, потому что HATEOAS в теории самоописывает себя.
Адам Гент
источник
19
Спасибо. JSend, в частности, это именно то, что я искал. Это похоже на то, что я делал, но имеет некоторые преимущества, которых нет у моего метода. Справедливости ради @trungly, JSend также очень близок к своему собственному ответу.
FtDRbwLXw6
8
В частности, для ответов об ошибках мне также нравится черновой вариант RFC для деталей API HTTP .
Питер Эннес
1
Может быть, вы хотите добавить code.google.com/p/json-service в список форматов описания?
emilesilvis
1
Я думаю, что ярлык «Рекомендуемый стандарт для Rails» немного преувеличен - это всего лишь решение одного программиста. Не уверен, что делает его «рекомендуемым стандартом» (особенно, если вы посмотрите на популярность драгоценного камня - не похоже, что многие люди используют это вообще)? Лично я не думаю, что большинство программистов Rails рекомендуют это решение из-за использования тела ответа вместо HTTP-заголовков для статуса
Иво Дзеччиароу,
2
Google JSON Style Guide также является хорошим справочником
MRodrigues
196

Руководство по Google JSON

Возвращение ответа успеха data

{
  "data": {
    "id": 1001,
    "name": "Wing"
  }
}

Ответ об ошибке возврата error

{
  "error": {
    "code": 404,
    "message": "ID not found"
  }
}

и если ваш клиент JS, вы можете использовать, if ("error" in response) {}чтобы проверить, есть ли ошибка.

Steely Wing
источник
13
Прежде всего, руководство Google JSON рекомендует использовать двойные кавычки вместо одинарных.
rpozarickij
1
Я не уверен, что вы можете справиться с этим с помощью JSON API на стороне сервера, такого как PlayJson, в любом случае это не имеет значения. @ Неужели твои ссылки не работают
Рис Брэдбери
3
Как насчет ошибок, которые должны предоставить список ошибок (например, проблемы проверки)?
Xeoncross
1
@Xeoncross нажмите на ссылку на слове error, страница Google дает пример этого
М.И. Райт
@Xeoncross Вы можете вернуть список сбоев, используя error.errors [], определяемый как: «Контейнер для любой дополнительной информации об ошибке. Если служба возвращает несколько ошибок, каждый элемент в массиве ошибок представляет отдельную ошибку». Возможно, в сообщении об ошибке верхнего уровня будет упоминаться «Запрос неудачной проверки ввода», а массив ошибок [] будет содержать одну запись для каждого конкретного сбоя проверки.
Джеймс Дейли
131

Я предполагаю, что стандарт де-факто действительно не появился (и, возможно, никогда). Но независимо от того, вот мое взятие:

Успешный запрос:

{
  "status": "success",
  "data": {
    /* Application-specific data would go here. */
  },
  "message": null /* Or optional success message */
}

Неудачный запрос:

{
  "status": "error",
  "data": null, /* or optional error payload */
  "message": "Error xyz has occurred"
}

Преимущество: одинаковые элементы верхнего уровня как в случае успеха, так и в случае ошибки

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

trungly
источник
3
да, это правильный путь, если вы используете POJO для разбора json! когда мы используем POJO, нам нужен статический, не динамический формат json!
LOG_TAG
Просто и по существу. На мой взгляд, лучше, чем jsend, потому что jsend отличает ошибку от ошибки.
Хосе Александр Ибарра
1
Я тоже использую этот шаблон, но с полем, messagesкоторое называется массивом сообщений, а не одной строкой.
StockBreak
4
Ответ - почти копия хорошо документированного JSend, который прост и очень полезен. Они предоставили третий статус failдля типичных проблем проверки, в то время errorкак используются только с фатальными ошибками, такими как ошибки БД.
s3m3n
для успеха: если он есть 200в заголовках, зачем вам вообще statusполе? просто верните объект данных прямо. Вы знаете, что это может вызвать дополнительные проблемы с типизированными языками FE, такими как TypeScript.
Денис М.
84

Предположим, вы задали вопрос о дизайне веб-сервисов REST, а точнее об успехе / ошибке.

Я думаю, что есть 3 различных типа дизайна.

  1. Используйте только код статуса HTTP, чтобы указать, была ли ошибка, и попытайтесь ограничить себя стандартными (обычно этого должно быть достаточно).

    • Плюсы: это стандарт, независимый от вашего API.
    • Минусы: меньше информации о том, что на самом деле произошло.
  2. Используйте HTTP Status + json body (даже если это ошибка). Определите единую структуру для ошибок (например: код, сообщение, причина, тип и т. Д.) И используйте ее для ошибок, если она успешна, просто верните ожидаемый ответ json.

    • Плюсы: все еще стандартно, так как вы используете существующие коды состояния HTTP и возвращаете json с описанием ошибки (вы предоставляете больше информации о том, что произошло).
    • Минусы: выходной JSON будет варьироваться в зависимости от того, если это ошибка или успех.
  3. Забудьте статус http (например, всегда status 200), всегда используйте json и добавляйте в корень ответа логический responseValid и объект ошибки (код, сообщение и т. Д.), Который будет заполнен, если это ошибка, в противном случае другие поля (успех) заселены.

    • Плюсы: клиент имеет дело только с телом ответа, который является строкой json, и игнорирует статус (?).

    • Минусы: менее стандартные.

Выбор за вами :)

В зависимости от API я бы выбрал 2 или 3 (я предпочитаю 2 для json rest apis). Еще одна вещь, которую я испытал при разработке REST Api, - это важность документации для каждого ресурса (url): параметров, тела, ответа, заголовков и т. Д. + Примеры.

Я также рекомендую вам использовать jersey (реализация jax-rs) + genson (библиотека привязки java / json). Вам нужно всего лишь добавить genson + jersey в classpath, и json автоматически поддерживается.

РЕДАКТИРОВАТЬ:

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

  • Решение 3 легко реализовать как на стороне сервера, так и на клиенте, но это не так хорошо, поскольку вам придется инкапсулировать объекты, которые вы хотите вернуть, в объект ответа, содержащий также ошибку responseValid +.

Eugen
источник
2
Вы говорите, что я должен «Определить единую структуру для ошибок» и другие подобные предложения, но это именно то, о чем я спрашиваю. Я предполагаю, что ответом будет то, что «нет, нет никаких стандартных или лучших практик в отношении этой структуры».
FtDRbwLXw6
7
Для записи: код состояния HTTP не является заголовком.
pepkin88
3
«Ответом будет не JSON, а HTML.» неправильно! HTML не имеет ничего общего с обработкой ошибок. ответ может быть любым типом контента, который вы поддерживаете.
oligofren
2
@ ア レ ッ ク ス Код состояния HTTP - это трехзначный код в строке состояния заголовка ответа HTTP. После этой строки идут поля заголовков, которые в разговорной речи также называются заголовками.
pepkin88
1
@ ア レ ッ ク ス Страница Википедии по HTTP приятно отвечает на ваши вопросы, вы можете проверить ее там: en.wikipedia.org/wiki/… (ссылка на раздел Ответное сообщение)
pepkin88
19

Следующее - формат Instagram, который использует Instagram

{
    "meta": {
         "error_type": "OAuthException",
         "code": 400,
         "error_message": "..."
    }
    "data": {
         ...
    },
    "pagination": {
         "next_url": "...",
         "next_max_id": "13872296"
    }
}
Мухаммед Амин
источник
19

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

Я предпочитаю краткий ответ (при запросе списка / статей я хочу массив статей JSON).

В моих проектах я использую HTTP для отчета о состоянии, 200 возвращает только полезную нагрузку.

400 возвращает сообщение о том, что не так с запросом:

{"message" : "Missing parameter: 'param'"}

Возврат 404, если модель / контроллер / URI не существует

Если произошла ошибка с обработкой на моей стороне, я возвращаю 501 с сообщением:

{"message" : "Could not connect to data store."}

Из того, что я видел, довольно много сред REST-ish имеют тенденцию быть в этом направлении.

Обоснование :

JSON должен быть форматом полезной нагрузки , а не протоколом сеанса. Вся идея подробных полезных нагрузок сессионного выхода исходит от мира XML / SOAP и различных ошибочных решений, которые создали эти раздутые проекты. После того, как мы поняли, что все это было огромной головной болью, весь смысл REST / JSON состоял в том, чтобы поцеловать его и придерживаться HTTP. Я не думаю, что в JSend есть что-то отдаленно стандартное, особенно среди более многословных. XHR будет реагировать на HTTP-ответ, если вы используете jQuery для своего AJAX (как это делают большинство), вы можете использовать try/ catchи done()/ fail()callbacks для захвата ошибок. Я не вижу, как инкапсуляция отчетов о состоянии в JSON более полезна, чем эта.

Боян Маркович
источник
2
Msgstr "JSON - это формат полезной нагрузки ...". Нет, JSON - это формат сериализации данных. Вы можете использовать его для передачи чего угодно, включая протоколы сеансов или просто полезные данные. Ваши комментарии поцелуев нацелены на цель и не зависят от JSON. Лучше держать JSON сосредоточенным на том, что это такое (данные об успехе или данные о причине сбоя, как вы описываете), чем загрязнять его некоторой путаницей, которую необходимо постоянно составлять, а затем удалять. Затем вы можете пройти весь путь и сохранить данные JSON как есть в Couchbase и вернуть их как есть приложению.
Дирк Бестер
1
Возможно, мне следовало бы сформулировать это как «предполагаемый формат полезной нагрузки», но помимо этого я поддерживаю свой комментарий. Вы можете поместить данные сеанса / ошибки в качестве атрибутов тега body в HTML-документ, но это не делает его правильным или разумным способом сделать это.
Боян Маркович
16

Для чего это стоит, я делаю это по-другому. Успешный вызов просто имеет объекты JSON. Мне не нужен объект JSON более высокого уровня, который содержит поле успеха, указывающее значение true, и поле полезной нагрузки, в котором есть объект JSON. Я просто возвращаю соответствующий объект JSON с 200 или любым другим в диапазоне 200 для статуса HTTP в заголовке.

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

{
  "description" : "Validation Failed"
  "errors" : [ {
    "field" : "phoneNumber",
    "message" : "Invalid phone number."
  } ],
}

Важными моментами здесь являются то, что свойство field должно точно соответствовать полю JSON, которое не может быть проверено. Это позволяет клиентам точно знать, что пошло не так с их запросом. Кроме того, «сообщение» находится в локали запроса. Если оба «emailAddress» и «phoneNumber» были недействительными, то массив «errors» будет содержать записи для обоих. Тело ответа JSON 409 (конфликт) может выглядеть так:

{
  "description" : "Already Exists"
  "errors" : [ {
    "field" : "phoneNumber",
    "message" : "Phone number already exists for another user."
  } ],
}

С помощью кода состояния HTTP и этого JSON у клиента есть все, что ему нужно для детерминированного реагирования на ошибки, и он не создает новый стандарт ошибок, который пытается завершить замену кодов состояния HTTP. Обратите внимание, что это происходит только для диапазона 400 ошибок. Для всего в диапазоне 200 я могу просто вернуть все, что подходит. Для меня это часто HAL-подобный объект JSON, но здесь это не имеет значения.

Единственное, что я подумал о добавлении, это числовой код ошибки либо в записях массива «errors», либо в корне самого объекта JSON. Но пока нам это не нужно.

robert_difalco
источник
9

Они не согласны с остальными форматами ответов API-интерфейсов крупных программных гигантов - Google, Facebook, Twitter, Amazon и др., Хотя в приведенных выше ответах было приведено много ссылок, где некоторые пытались стандартизировать формат ответов.

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

Ниже приводится мое мнение о формате ответов, вдохновленном Google, Twitter, Amazon и некоторыми публикациями в Интернете:

https://github.com/adnan-kamili/rest-api-response-format

Swagger файл:

https://github.com/adnan-kamili/swagger-sample-template

Аднан Камили
источник
1
upvote для формата rest-api-response-без
конвертов
@adnan kamilli - >>> StatusCode: 304, ReasonPhrase: «Не изменено», Версия: 1.1, Содержимое: <null>, Заголовки: {} <<<< это правильный ответ restApi?
Арнольд Браун
@ArnoldBrown Для какой конечной точки API - действие вы возвращаете этот код?
Аднан Камили
Это ответ API-интерфейса, используемого для загрузки изображения (данные формы) - Клиентский API-интерфейс.
Арнольд Браун
7

Дело в том, что JSON полностью динамичен и гибок. Согните его по своему усмотрению, потому что это просто набор сериализованных объектов и массивов JavaScript, коренящихся в одном узле.

Какой тип корневого узла зависит от вас, от того, что он содержит, зависит от вас, от вас зависит, отправляете ли вы метаданные вместе с ответом, устанавливаете ли вы mime-тип application/jsonили оставляете его text/plainна ваше усмотрение ( до тех пор, пока вы знаете, как обрабатывать крайние случаи).

Создайте легкую схему, которая вам нравится.
Лично я обнаружил , что аналитика отслеживания и mp3 / OGG сервировки и изображения галереи сервировки и текстовых сообщений и сетевые-пакеты для онлайн - игр, и блог-посты и блог-комментарии все имеют очень разные требования в плане того , что отправлено и что получено и как их нужно употреблять.

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

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

Norguard
источник
1
Спасибо за ответ, но опять же, я не беспокоюсь о самих полезных нагрузках. Хотя все ваши примеры предъявляют очень разные требования в отношении того, что отправляется / получается в полезных нагрузках и как эти полезные нагрузки потребляются, все они должны решать одни и те же проблемы в отношении самого ответа . А именно, все они должны определить, был ли запрос успешным. Если это так, приступайте к обработке. Если это не так, что пошло не так. Именно этот шаблон является общим для всех ответов API, на которые я ссылаюсь в своем вопросе.
FtDRbwLXw6
Либо верните статус 200 для всего, и определите себе конкретную полезную нагрузку ошибки, либо верните статус, соразмерный с ошибкой, с и / или без дополнительных подробностей в теле полезной нагрузки (если поддерживается). Как я уже сказал, схема зависит от вас - включая любую мета / информацию о состоянии. Это на 100% чистый лист, чтобы делать то, что вам нравится, исходя из вашего предпочтительного стиля архитектуры.
Norguard
2
Я понимаю, что это чистый список дел, как мне угодно. Цель моего вопроса - спросить, были ли какие-либо новые стандарты в отношении структуры. Я не спрашивал «что такое JSON и как его использовать», а скорее «я знаю, как использовать JSON для возврата / структурирования всего, что я хочу, но я хотел бы знать, используются ли какие-либо стандартные структуры или становится популярным. " Извините, если я неправильно сформулировал вопрос. В любом случае, спасибо за ваш ответ.
FtDRbwLXw6
7

JSON-RPC 2.0 определяет стандартный формат запросов и ответов и является глотком свежего воздуха после работы с REST API.

dnault
источник
Единственное, что JSON-RPC_2.0 предлагает для исключений, это код ошибки? Числовой код ошибки не может с точностью показать возникшую проблему.
AgilePro
@AgilePro Согласен, числовой код ошибки не очень хорош, и я хотел бы, чтобы авторы спецификации позволили codeполю быть строкой. К счастью, спецификация позволяет нам помещать любую информацию, которую мы хотим, в поле ошибки data. В моих проектах JSON-RPC я обычно использую один числовой код для всех ошибок прикладного уровня (в отличие от одной из стандартных ошибок протокола). Затем я поместил подробную информацию об ошибке (включая строковый код, указывающий реальный тип ошибки) в dataполе.
Дне
2

Для тех, кто придет позже, в дополнение к принятому ответу, который включает HAL, JSend и JSON API, я бы добавил несколько других спецификаций, на которые стоит обратить внимание:

  • JSON-LD , который является Рекомендацией W3C и определяет, как создавать совместимые веб-сервисы в JSON
  • Ion Hypermedia Type для REST, который заявляет о себе как «простой и интуитивно понятный гипермедиа тип на основе JSON для REST»
А. Баракат
источник
1

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

Я провел небольшое исследование и обнаружил, что наиболее распространенным форматом для возврата ошибки (исключения) является структура этой формы:

{
   "success": false,
   "error": {
      "code": "400",
      "message": "main error message here",
      "target": "approx what the error came from",
      "details": [
         {
            "code": "23-098a",
            "message": "Disk drive has frozen up again.  It needs to be replaced",
            "target": "not sure what the target is"
         }
      ],
      "innererror": {
         "trace": [ ... ],
         "context": [ ... ]
      }
   }
}

Это формат, предложенный стандартом OASIS для данных OASIS OData, и он, кажется, является наиболее стандартным вариантом, однако пока не наблюдается высоких показателей принятия какого-либо стандарта. Этот формат соответствует спецификации JSON-RPC.

Вы можете найти полную библиотеку с открытым исходным кодом, которая реализует это по адресу: Mendocino JSON Utilities . Эта библиотека поддерживает объекты JSON, а также исключения.

Подробности обсуждаются в моем блоге об обработке ошибок в JSON REST API.

AgilePro
источник
0

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

Почти во всех предложенных случаях я вижу отдельные ответы для сценария «Успех» и «Ошибка», что для меня является двусмысленной. Если ответы в этих двух случаях различны, то почему нам действительно нужно поставить флаг «Успех»? Разве не очевидно, что отсутствие «ошибки» является «успехом»? Возможно ли получить ответ, в котором «Успех» равен «ИСТИНА» с установленным «Ошибка»? Или, кстати, «Успех» - ЛОЖЬ, а «Ошибка» не установлена? Только одного флага недостаточно? Я бы предпочел иметь только флаг «Ошибка», потому что я считаю, что будет меньше «Ошибка», чем «Успех».

Кроме того, мы должны действительно сделать флаг «Ошибка»? Что делать, если я хочу ответить с несколькими ошибками проверки? Итак, я считаю более эффективным иметь узел «Ошибка» с каждой ошибкой как дочерней по отношению к этому узлу; где пустой (считается до нуля) узел «Ошибка» будет означать «Успех».

Сломанная стрела
источник
-2

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

Это для ответа «Успех»

{  
   "ReturnCode":"1",
   "ReturnMsg":"Successfull Transaction",
   "ReturnValue":"",
   "Data":{  
      "EmployeeName":"Admin",
      "EmployeeID":1
   }
}

Это для ответа «Ошибка»

{
    "ReturnCode": "4",
    "ReturnMsg": "Invalid Username and Password",
    "ReturnValue": "",
    "Data": {}
}
Маниш Вадхер
источник
2
Было бы лучше стандартизировать ваши свойства. Все они являются значениями «Возврат ...». Но данные не имеют префикса. Я бы сказал, отбросьте все префиксы «Return».
z0mbi3
Включение «возврата» также довольно излишне.
Джек Маркетти