Formato de respuesta API JSON estándar?

697

¿Existen estándares o mejores prácticas para estructurar respuestas JSON desde una API? Obviamente, los datos de cada aplicación son diferentes, por lo que no me preocupa mucho, sino más bien la "placa de respuesta", si lo desea. Un ejemplo de lo que quiero decir:

Solicitud exitosa:

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

Solicitud fallida:

{
  "success": false,
  "payload": {
    /* Application-specific data would go here. */
  },
  "error": {
    "code": 123,
    "message": "An error occurred!"
  }
}
FtDRbwLXw6
fuente
16
La gente probablemente ha aprendido de SOAP y no lo volverá a construir ...
Denys Séguret
18
@distroy: ¿Te importaría explicar tu comentario?
FtDRbwLXw6
55
Esta pregunta me interesó mucho, ya que recientemente tuve que diseñar una API JSON y me pregunté si eran estándares que definieran un formato de respuesta. El tuyo en realidad se ve bastante bien, y vale la pena usarlo si no encuentras un estándar. Es una pena que las respuestas proporcionadas en realidad no aborden la pregunta.
Alex
13
@Alex desafortunadamente, eso es porque no importa a dónde vayas, no hay un estándar. No solo dentro de JSON, sino en términos de cómo usarlo para aplicaciones RESTful, o cualquier otra cosa por el estilo. Todos lo hacen de manera diferente. Puede sentirse libre de seguir las mejores prácticas (respuestas HTTP, estructura de paquete significativa, un ojo hacia la estructuración de sus datos para el consumo de su sistema), pero todos los que son distribuidores importantes están haciendo al menos una cosa diferente a las demás. .. No hay un estándar, y es probable que no lo haya, así que construya algo sólido y hágalo a su medida.
Norguard
55
@Norguard hay estándares (ver mi respuesta). De hecho, lo bueno de los estándares es que tienes tantos para elegir. - Andrew Tanenbaum
Adam Gent

Respuestas:

641

Sí, hay un par de estándares (aunque algunas libertades en la definición de estándar) que han surgido:

  1. API JSON: la API JSON cubre la creación y actualización de recursos también, no solo las respuestas.
  2. JSend : simple y probablemente lo que ya estás haciendo.
  3. Protocolo OData JSON : muy complicado.
  4. HAL : como OData pero con el objetivo de ser como HATEOAS .

También hay formatos de descripción de API JSON:

Adam Gent
fuente
19
Gracias. JSend en particular es exactamente lo que estaba buscando. Es similar a lo que estaba haciendo, pero tiene algunos beneficios que mi método no tuvo. Para ser justos con @trungly, JSend también está muy cerca de su propia respuesta.
FtDRbwLXw6
8
Para respuestas de error específicamente, también me gustan los Detalles del problema para el borrador de RFC de las API HTTP .
Pieter Ennes
1
¿Quizás desee agregar code.google.com/p/json-service a la lista de formatos de descripción?
emilesilvis
1
Creo que la etiqueta "Un estándar recomendado para Rails" es un poco exagerada: esta es solo la solución de un programador. ¿No está seguro de qué lo convierte en un "estándar recomendado" (especialmente si observa la popularidad de la gema, no parece que muchas personas lo estén usando)? Personalmente, no creo que la mayoría de los programadores de Rails recomendarían esta solución debido al uso del cuerpo de respuesta en lugar de los encabezados HTTP para el estado
Iwo Dziechciarow
2
La Guía de estilo JSON de Google también es una buena referencia
MRodrigues
195

Guía de Google JSON

Respuesta de respuesta exitosa data

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

Respuesta de error volver error

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

y si su cliente es JS, puede usarlo if ("error" in response) {}para verificar si hay un error.

Ala de acero
fuente
13
En primer lugar, la guía JSON de Google recomienda usar comillas dobles en lugar de comillas simples.
rpozarickij
1
No estoy seguro de si puede manejar esto desde una API JSON del lado del servidor como PlayJson, de cualquier manera, no importa. @Steely tus enlaces están rotos
Rhys Bradbury
3
¿Qué pasa con los errores que deben proporcionar una lista de fallas (como problemas de validación)?
Xeoncross
1
@Xeoncross haga clic en el enlace de la palabra error, la página de Google da un ejemplo de esto
MI Wright,
@Xeoncross Puede devolver una lista de fallas usando error.errors [], definido como: "Contenedor para cualquier información adicional sobre el error. Si el servicio devuelve múltiples errores, cada elemento en la matriz de errores representa un error diferente". Quizás el error de nivel superior mencionaría "Solicitud de validación de entrada fallida" y la matriz de errores [] tendría una entrada para cada falla de validación específica que ocurriera.
James Daily el
130

Supongo que un estándar de facto no ha surgido realmente (y puede que nunca). Pero independientemente, aquí está mi opinión:

Solicitud exitosa:

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

Solicitud fallida:

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

Ventaja: los mismos elementos de nivel superior tanto en casos de éxito como de error

Desventaja: no hay código de error, pero si lo desea, puede cambiar el estado para que sea un código (correcto o incorrecto), o puede agregar otro elemento de nivel superior denominado "código".

trungly
fuente
3
sí, ¡esta es la forma correcta si está utilizando POJO para el análisis json! ¡Cuando usamos POJOs necesitamos formato json estático, no dinámico!
LOG_TAG
Simple y al grano. Mejor que jsend en mi opinión porque jsend distingue el error del error.
Josue Alexander Ibarra
1
También uso este patrón pero con un campo llamado messagesque es una matriz de mensajes en lugar de una sola cadena.
StockBreak
44
La respuesta es casi una copia de JSend bien documentado, que es simple y muy útil. Proporcionaron un tercer estado failpara los problemas de validación típicos, mientras errorque solo se usa con fatalidades como errores de db.
s3m3n
para el éxito: si lo tiene 200en los encabezados, ¿por qué necesita un statuscampo? simplemente devuelva el objeto de datos directamente. Sabes que esto puede causar dolor adicional con lenguajes FE escritos como TypeScript.
Deniss M.
84

Asumiendo que su pregunta es sobre el diseño de servicios web REST y más precisamente sobre el éxito / error.

Creo que hay 3 tipos diferentes de diseño.

  1. Use solo el código de estado HTTP para indicar si hubo un error e intente limitarse a los estándares (generalmente debería ser suficiente).

    • Pros: es un estándar independiente de su API.
    • Contras: Menos información sobre lo que realmente sucedió.
  2. Use HTTP Status + json body (incluso si es un error). Defina una estructura uniforme para errores (ej .: código, mensaje, razón, tipo, etc.) y úsela para errores, si es un éxito, simplemente devuelva la respuesta json esperada.

    • Pros: sigue siendo estándar cuando usa los códigos de estado HTTP existentes y devuelve un json que describe el error (proporciona más información sobre lo que sucedió).
    • Contras: La salida json variará dependiendo de si es un error o un éxito.
  3. Olvide el estado http (ej .: siempre estado 200), use siempre json y agregue en la raíz de la respuesta una respuesta booleana Válida y un objeto de error (código, mensaje, etc.) que se completará si se trata de un error; de lo contrario, los otros campos (éxito) están poblados.

    • Pros: el cliente trata solo con el cuerpo de la respuesta que es una cadena json e ignora el estado (?).

    • Contras: El menos estándar.

Depende de usted elegir :)

Dependiendo de la API, elegiría 2 o 3 (prefiero 2 para json rest apis). Otra cosa que he experimentado en el diseño de REST Api es la importancia de la documentación para cada recurso (url): los parámetros, el cuerpo, la respuesta, los encabezados, etc. + ejemplos.

También le recomendaría que use jersey (implementación jax-rs) + genson (biblioteca de enlace de datos java / json). Solo tienes que soltar genson + jersey en tu classpath y json es compatible automáticamente.

EDITAR:

  • La solución 2 es la más difícil de implementar, pero la ventaja es que puede manejar muy bien las excepciones y no solo los errores comerciales, el esfuerzo inicial es más importante sino que usted gana a largo plazo.

  • La solución 3 es fácil de implementar tanto en el lado del servidor como en el cliente, pero no es tan agradable, ya que tendrá que encapsular los objetos que desea devolver en un objeto de respuesta que contenga también el error responseValid +.

Eugen
fuente
2
Usted dice que debería "Definir una estructura uniforme para errores" y otras sugerencias similares, pero esto es precisamente lo que estoy preguntando. Supongo que la respuesta es que "no, no hay prácticas estándar o mejores con respecto a esta estructura".
FtDRbwLXw6
77
Para el registro: el código de estado HTTP no es un encabezado.
pepkin88
3
"la respuesta no será json sino html". ¡incorrecto! HTML no tiene nada que ver con el manejo de errores. la respuesta puede ser cualquier tipo de contenido que admita.
oligofren
2
@ ア レ ッ ク ス El código de estado HTTP es un código de 3 dígitos en la línea de estado del encabezado de una respuesta HTTP. Siguiendo esa línea están los campos de encabezado, coloquialmente también llamados encabezados.
pepkin88
1
@ ア レ ッ ク ス La página de Wikipedia en HTTP responde muy bien a sus preguntas, puede consultarla allí: en.wikipedia.org/wiki/… (enlace a la sección Mensaje de respuesta)
pepkin88
19

El siguiente es el formato json que instagram está usando

{
    "meta": {
         "error_type": "OAuthException",
         "code": 400,
         "error_message": "..."
    }
    "data": {
         ...
    },
    "pagination": {
         "next_url": "...",
         "next_max_id": "13872296"
    }
}
Muhammad Amin
fuente
19

No seré tan arrogante para afirmar que este es un estándar, así que usaré el formulario "Prefiero".

Prefiero una respuesta breve (cuando solicito una lista de / artículos quiero una matriz JSON de artículos).

En mis diseños utilizo HTTP para el informe de estado, un 200 devuelve solo la carga útil.

400 devuelve un mensaje de lo que estaba mal con la solicitud:

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

Devuelve 404 si el modelo / controlador / URI no existe

Si hubo un error con el procesamiento de mi parte, devuelvo 501 con un mensaje:

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

Por lo que he visto, bastantes marcos REST-ish tienden a estar en esta línea.

Justificación :

Se supone que JSON es un formato de carga útil , no es un protocolo de sesión. La idea completa de las cargas útiles detalladas de sesión proviene del mundo XML / SOAP y varias opciones equivocadas que crearon esos diseños hinchados. Después de que nos dimos cuenta de que todo era un dolor de cabeza masivo, el objetivo de REST / JSON era BESARLO y adherirse a HTTP. No creo que haya nada remotamente estándar en JSend y especialmente no con los más detallados entre ellos. XHR reaccionará a la respuesta HTTP, si usa jQuery para su AJAX (como la mayoría lo hace) puede usar try/ catchy done()/ fail()callbacks para capturar errores. No puedo ver cómo encapsular informes de estado en JSON es más útil que eso.

Bojan Markovic
fuente
2
"JSON es un formato de carga útil ...". No, JSON es un formato de serialización de datos. Puede usarlo para transmitir lo que desee, incluidos protocolos de sesión o simplemente cargas útiles. Sin embargo, sus comentarios de KISS son objetivos e independientes de JSON. Es mejor mantener el JSON enfocado en lo que es (datos de éxito o datos de razón de falla como usted describe) que contaminarlo con un poco de mezcla de ambos que constantemente tiene que componerse y luego eliminarse. Luego puede ir hasta el final y almacenar sus datos JSON tal como están en Couchbase y devolverlos tal como están a la aplicación.
Dirk Bester
1
Quizás debería haberlo formulado como "se supone que es un formato de carga útil", pero aparte de eso, mantengo mi comentario. Puede colocar datos de sesión / error como atributos de la etiqueta del cuerpo en el documento HTML, pero eso no lo convierte en la forma correcta o sensata de hacerlo.
Bojan Markovic
16

Por lo que vale, hago esto de manera diferente. Una llamada exitosa solo tiene los objetos JSON. No necesito un objeto JSON de nivel superior que contenga un campo de éxito que indique verdadero y un campo de carga útil que tenga el objeto JSON. Acabo de devolver el objeto JSON apropiado con un 200 o lo que sea apropiado en el rango de 200 para el estado HTTP en el encabezado.

Sin embargo, si hay un error (algo en la familia 400) devuelvo un objeto de error JSON bien formado. Por ejemplo, si el cliente está PUBLICANDO a un usuario con una dirección de correo electrónico y un número de teléfono y uno de ellos está mal formado (es decir, no puedo insertarlo en mi base de datos subyacente), devolveré algo como esto:

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

Los bits importantes aquí son que la propiedad "campo" debe coincidir exactamente con el campo JSON que no se pudo validar. Esto permite a los clientes saber exactamente qué salió mal con su solicitud. Además, "mensaje" está en la configuración regional de la solicitud. Si tanto la "dirección de correo electrónico" como el "número de teléfono" no eran válidos, la matriz de "errores" contendría entradas para ambos. Un cuerpo de respuesta JSON 409 (Conflicto) podría verse así:

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

Con el código de estado HTTP y este JSON, el cliente tiene todo lo que necesita para responder a los errores de manera determinista y no crea un nuevo estándar de error que intente completar el reemplazo de los códigos de estado HTTP. Tenga en cuenta que esto solo ocurre para el rango de 400 errores. Para cualquier cosa en el rango 200, puedo devolver lo que sea apropiado. Para mí, a menudo es un objeto JSON similar a HAL, pero eso realmente no importa aquí.

Lo único que pensé en agregar fue un código de error numérico en las entradas de la matriz de "errores" o en la raíz del objeto JSON. Pero hasta ahora no lo hemos necesitado.

robert_difalco
fuente
9

No hay acuerdo sobre el resto de los formatos de respuesta de API de los grandes gigantes del software: Google, Facebook, Twitter, Amazon y otros, aunque se han proporcionado muchos enlaces en las respuestas anteriores, donde algunas personas han tratado de estandarizar el formato de respuesta.

Como las necesidades de las API pueden diferir, es muy difícil que todos participen y acepten algún formato. Si tiene millones de usuarios utilizando su API, ¿por qué cambiaría su formato de respuesta?

Lo siguiente es mi opinión sobre el formato de respuesta inspirado en Google, Twitter, Amazon y algunas publicaciones en Internet:

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

Archivo Swagger:

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

adnan kamili
fuente
1
voto a favor para el formato de respuesta de descanso-api sin sobres
Kerem Baydoğan
@adnan kamilli - >>> StatusCode: 304, ReasonPhrase: 'Not Modified', Version: 1.1, Content: <null>, Headers: {} <<<< ¿es esta una respuesta adecuada de restApi?
Arnold Brown
@ArnoldBrown ¿Para qué punto final de API - acción está devolviendo este código?
adnan kamili
es una respuesta de respuesta de una API utilizada para cargar una imagen (datos del formulario) - La API escrita por el cliente.
Arnold Brown
7

El punto de JSON es que es completamente dinámico y flexible. Inclínelo a cualquier capricho que desee, porque es solo un conjunto de objetos y matrices de JavaScript serializados, enraizados en un solo nodo.

El tipo de rootnode depende de usted, lo que contiene depende de usted, si envía metadatos junto con la respuesta depende de usted, si configura el tipo MIME application/jsono lo deja como text/plaindepende de usted ( siempre y cuando sepa cómo manejar los casos límite).

Crea un esquema ligero que te guste.
En lo personal, he encontrado que el análisis de seguimiento y MP3 / Ogg porción y la imagen a la galería de servicio y los mensajes de texto y de red-paquetes para juegos en línea y blog-mensajes y el blog-comenta todas tienen necesidades muy diferentes en términos de lo que es enviado y lo que se recibe y cómo deben ser consumidos.

Entonces, lo último que me gustaría, al hacer todo eso, es tratar de hacer que cada uno se ajuste al mismo estándar estándar, que se basa en XML2.0 o somesuch.

Dicho esto, hay mucho que decir sobre el uso de esquemas que tienen sentido para usted y están bien pensados.
Simplemente lea algunas respuestas API, tenga en cuenta lo que le gusta, critique lo que no, escriba esas críticas y comprenda por qué lo molestan de la manera incorrecta, y luego piense en cómo aplicar lo que aprendió a lo que necesita.

Norguard
fuente
1
Gracias por la respuesta, pero de nuevo, no estoy preocupado por las cargas en sí mismas. Si bien todos sus ejemplos tienen requisitos muy diferentes en términos de lo que se envía / recibe dentro de las cargas útiles y cómo se consumen esas cargas útiles , todos tienen que resolver los mismos problemas con respecto a la respuesta en sí . Es decir, todos necesitan determinar si la solicitud fue exitosa. Si fue así, proceda con el procesamiento. Si no fue así, qué salió mal. Es esta repetitiva la que es común a todas las respuestas API a las que me refiero en mi pregunta.
FtDRbwLXw6
O devuelve un estado de 200 para todo, y define una carga útil de error específica, o devuelve un estado acorde con el error, con y / o sin más detalles en el cuerpo de la carga útil (si es compatible). Como dije, el esquema depende de usted, incluida cualquier información de meta / estado. Es una pizarra 100% en blanco para hacer con lo que desee según su estilo de arquitectura preferido.
Norguard
2
Me doy cuenta de que es una pizarra en blanco para hacer lo que quiera. El propósito de mi pregunta es preguntar si había estándares emergentes en lo que respecta a la estructura. No estaba preguntando "qué es JSON y cómo lo uso", sino más bien, "sé cómo usar JSON para devolver / estructurar todo lo que quiero, pero me gustaría saber si se están utilizando estructuras estándar o llegando a ser popular." Lo siento si me equivoqué con una pregunta. Gracias por su respuesta, de todos modos.
FtDRbwLXw6
7

JSON-RPC 2.0 define un formato estándar de solicitud y respuesta, y es una bocanada de aire fresco después de trabajar con las API REST.

dnault
fuente
¿Lo único que JSON-RPC_2.0 ofrece para excepciones es un código de error? Un código de error numérico no puede representar con fidelidad el problema que ocurrió.
AgilePro
@AgilePro De acuerdo, un código de error numérico no es muy bueno, y desearía que los autores de la especificación hubieran permitido que el codecampo sea una Cadena. Afortunadamente, la especificación nos permite introducir cualquier información que queramos en el datacampo del error . En mis proyectos JSON-RPC, normalmente uso un solo código numérico para todos los errores de la capa de aplicación (en lugar de uno de los errores de protocolo estándar). Luego pongo la información detallada del error (incluido un código de cadena que indica el tipo de error real) en el datacampo.
dnault
2

Para los que vengan después, además de la respuesta aceptada que incluye HAL, JSend y JSON API, agregaría algunas otras especificaciones que vale la pena considerar:

  • JSON-LD , que es una recomendación del W3C y especifica cómo construir servicios web interoperables en JSON
  • Ion Hypermedia Type for REST, que se afirma como "un tipo de hipermedia basado en JSON simple e intuitivo para REST"
A. Barakat
fuente
1

El marco básico sugerido se ve bien, pero el objeto de error como se define es demasiado limitado. A menudo no se puede usar un solo valor para expresar el problema, y ​​en su lugar se necesita una cadena de problemas y causas .

Investigué un poco y descubrí que el formato más común para devolver el error (excepciones) es una estructura de esta forma:

{
   "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": [ ... ]
      }
   }
}

Este es el formato propuesto por el estándar de datos OASIS OASIS OData y parece ser la opción más estándar que existe, sin embargo, no parece haber altas tasas de adopción de ningún estándar en este momento. Este formato es consistente con la especificación JSON-RPC.

Puede encontrar la biblioteca completa de código abierto que implementa esto en: Mendocino JSON Utilities . Esta biblioteca admite los objetos JSON, así como las excepciones.

Los detalles se discuten en mi publicación de blog sobre Manejo de errores en JSON REST API

AgilePro
fuente
0

No existe un estándar de infracción de la ley o fuera de la ley que no sea el sentido común. Si resumimos esto como dos personas hablando, el estándar es la mejor manera en que pueden entenderse con precisión en palabras mínimas en un tiempo mínimo. En nuestro caso, "palabras mínimas" es optimizar el ancho de banda para la eficiencia del transporte y "comprender con precisión" es la estructura para la eficiencia del analizador sintáctico; que finalmente termina con menos datos y la estructura común; para que pueda pasar a través de un agujero de alfiler y se pueda analizar a través de un alcance común (al menos inicialmente).

Casi en todos los casos sugeridos, veo respuestas separadas para el escenario 'Éxito' y 'Error', lo cual es una especie de ambigüedad para mí. Si las respuestas son diferentes en estos dos casos, ¿por qué realmente necesitamos poner una bandera de 'Éxito' allí? ¿No es obvio que la ausencia de 'Error' es un 'Éxito'? ¿Es posible tener una respuesta donde 'Éxito' es VERDADERO con un conjunto 'Error'? ¿O la forma en que 'Éxito' es FALSO sin establecer 'Error'? ¿Una sola bandera no es suficiente? Preferiría tener solo el indicador 'Error', porque creo que habrá menos 'Error' que 'Éxito'.

Además, ¿deberíamos realmente hacer que el 'Error' sea una bandera? ¿Qué pasa si quiero responder con múltiples errores de validación? Entonces, me parece más eficiente tener un nodo 'Error' con cada error como hijo de ese nodo; donde un nodo 'Error' vacío (cuenta hasta cero) denotaría un 'Éxito'.

Flecha rota
fuente
-2

La mejor respuesta para las API web que los desarrolladores móviles pueden comprender fácilmente.

Esto es para la respuesta de "éxito"

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

Esto es para la respuesta de "Error"

{
    "ReturnCode": "4",
    "ReturnMsg": "Invalid Username and Password",
    "ReturnValue": "",
    "Data": {}
}
Vadher Manish
fuente
2
Sería mejor estandarizar sus propiedades. Todos son valores de "Devolución ...". Pero los datos no tienen prefijo. Yo diría que suelte todos los prefijos de "Retorno".
z0mbi3
Incluir "Retorno" también es bastante redundante.
Jack Marchetti