Formato di risposta API JSON standard?


697

Esistono standard o best practice per strutturare le risposte JSON da un'API? Ovviamente, i dati di ogni applicazione sono diversi, quindi non mi preoccupo molto, ma piuttosto la "risposta", se vuoi. Un esempio di cosa intendo:

Richiesta riuscita:

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

Richiesta non riuscita:

{
  "success": false,
  "payload": {
    /* Application-specific data would go here. */
  },
  "error": {
    "code": 123,
    "message": "An error occurred!"
  }
}

16
Probabilmente le persone hanno imparato da SOAP e non lo costruiranno più ...
Denys Séguret,

18
@dystroy: ti interessa spiegare il tuo commento?
FtDRbwLXw6,

5
Sono stato davvero interessato a questa domanda in quanto ho dovuto progettare un'API JSON di recente e mi sono trovato a chiedermi se fossero standard che definiscono un formato di risposta. In realtà il tuo sembra piuttosto carino e sembra che valga la pena usarlo se non trovi uno standard. È un peccato che le risposte fornite non rispondano effettivamente alla domanda.
Alex,

13
@Alex purtroppo, perché non importa dove tu vada, non esiste uno standard. Non solo all'interno di JSON stesso, ma in termini di come utilizzarlo per le applicazioni RESTful o qualsiasi altra cosa del genere. Lo fanno tutti diversamente. Puoi sentirti libero di seguire le migliori pratiche (risposte HTTP, struttura dei pacchetti significativa, un occhio alla strutturazione dei tuoi dati per il consumo da parte del tuo sistema), ma tutti coloro che sono un grande distributore stanno facendo almeno una cosa diversa dalle altre. .. Non esiste uno standard e probabilmente non ce ne sarà uno, quindi costruisci qualcosa di solido e costruiscilo per adattarlo a te.
Norguard,

5
@Norguard ci sono degli standard (vedi la mia risposta). In effetti, la cosa bella degli standard è che hai così tanti tra cui scegliere. - Andrew Tanenbaum
Adam Gent,

Risposte:


641

Sì, sono emersi un paio di standard (anche se alcune libertà sulla definizione di standard) che sono emersi:

  1. API JSON - L' API JSON copre anche la creazione e l'aggiornamento delle risorse, non solo le risposte.
  2. JSend - Semplice e probabilmente quello che stai già facendo.
  3. Protocollo JSON OData - Molto complicato.
  4. HAL - Come OData, ma mirando ad essere HATEOAS .

Esistono anche formati di descrizione dell'API JSON:


19
Grazie. JSend in particolare è esattamente quello che stavo cercando. È simile a quello che stavo facendo, ma ha alcuni benefici che il mio metodo non ha fatto. In tutta sincerità con @trungly, anche JSend è molto vicino alla sua stessa risposta.
FtDRbwLXw6,

8
Per le risposte agli errori, in particolare, mi piacciono anche le bozze RFC Dettagli sui problemi per le API HTTP .
Pieter Ennes,

1
Forse desideri aggiungere code.google.com/p/json-service all'elenco del formato della descrizione?
emilesilvis,

1
Penso che l'etichetta "Uno standard raccomandato per Rails" sia un po 'un'esagerazione: questa è solo la soluzione di un programmatore. Non sei sicuro di ciò che lo rende uno "standard raccomandato" (specialmente se osservi la popolarità della gemma - non sembra che molte persone lo stiano usando)? Personalmente non credo che la maggior parte dei programmatori di Rails raccomanderebbe questa soluzione a causa dell'utilizzo del corpo della risposta anziché delle intestazioni HTTP per lo stato
Iwo Dziechciarow

2
Google JSON Style Guide è anche un buon riferimento
MRodrigues

195

Guida di Google JSON

Risposta risposta riuscita data

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

Ritorno risposta errore error

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

e se il tuo client è JS, puoi usare if ("error" in response) {}per verificare se c'è un errore.


13
Prima di tutto, la guida di Google JSON consiglia di utilizzare virgolette doppie anziché singole.
rpozarickij,

1
Non sono sicuro di poterlo gestire da un'API JSON lato server come PlayJson, in entrambi i casi non importa. @Steely i tuoi collegamenti sono interrotti
Rhys Bradbury,

3
Che dire degli errori che devono fornire un elenco di errori (come i problemi di convalida)?
Xeoncross il

1
@Xeoncross fai clic sul collegamento sulla parola error, la pagina di Google ne fornisce un esempio
MI Wright il

@Xeoncross È possibile restituire un elenco di errori utilizzando error.errors [], definito come: "Contenitore per qualsiasi ulteriore informazione relativa all'errore. Se il servizio restituisce più errori, ogni elemento nell'array degli errori rappresenta un errore diverso." Forse l'errore di livello superiore menzionerebbe "Richiesta convalida input non riuscita" e l'array errors [] avrebbe una voce per ogni errore di convalida specifico che si è verificato.
James Daily,

130

Immagino che uno standard defacto non sia davvero emerso (e potrebbe non mai). Ma a prescindere, ecco la mia opinione:

Richiesta riuscita:

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

Richiesta non riuscita:

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

Vantaggio: stessi elementi di massimo livello sia in casi di successo che di errore

Svantaggio: nessun codice di errore, ma se lo si desidera, è possibile modificare lo stato in un codice (positivo o negativo), oppure - È possibile aggiungere un altro elemento di livello superiore denominato "codice".


3
sì, questo è il modo giusto se stai usando POJO per l'analisi json! quando utilizziamo POJO abbiamo bisogno di un formato json statico e non dinamico!
LOG_TAG

Semplice e al punto. Meglio di jsend secondo me perché jsend distingue l'errore dal fallimento.
Josue Alexander Ibarra,

1
Uso anche questo modello ma con un campo chiamato messagesche è una matrice di messaggi anziché una singola stringa.
StockBreak

4
La risposta è quasi una copia di JSend ben documentata, che è semplice e molto utile. Hanno fornito il terzo stato failper i tipici problemi di validazione, mentre errorè usato solo con fatali come errori db.
s3m3n,

per il successo: se è presente 200nelle intestazioni, perché hai persino bisogno di un statuscampo? restituisci semplicemente l'oggetto dati. Sai che questo può causare ulteriore dolore con linguaggi FE digitati come TypeScript.
Deniss M.,

84

Supponendo che la tua domanda riguardi la progettazione di servizi web REST e più precisamente riguardo al successo / errore.

Penso che ci siano 3 diversi tipi di design.

  1. Usa solo il codice di stato HTTP per indicare se si è verificato un errore e prova a limitarti a quelli standard (di solito dovrebbe bastare).

    • Pro: è uno standard indipendente dal tuo api.
    • Contro: meno informazioni su ciò che è realmente accaduto.
  2. Usa HTTP Status + json body (anche se si tratta di un errore). Definire una struttura uniforme per gli errori (es: codice, messaggio, motivo, tipo, ecc.) E usarla per gli errori, se ha esito positivo restituire semplicemente la risposta json prevista.

    • Pro: sempre standard quando si utilizzano i codici di stato HTTP esistenti e si restituisce un json che descrive l'errore (si forniscono ulteriori informazioni su ciò che è accaduto).
    • Contro: l'output json varierà a seconda che si tratti di un errore o di successo.
  3. Dimentica lo stato http (es: sempre stato 200), usa sempre json e aggiungi alla radice della risposta una risposta booleana valida e un oggetto di errore (codice, messaggio, ecc.) Che verrà popolato se si tratta di un errore, altrimenti gli altri campi (successo) sono popolati.

    • Pro: il client si occupa solo del corpo della risposta che è una stringa json e ignora lo stato (?).

    • Contro: meno standard.

Sta a te scegliere :)

A seconda dell'API sceglierei 2 o 3 (preferisco 2 per json rest apis). Un'altra cosa che ho sperimentato nella progettazione di REST Api è l'importanza della documentazione per ogni risorsa (url): i parametri, il corpo, la risposta, le intestazioni ecc. + Esempi.

Ti consiglierei anche di usare jersey (implementazione jax-rs) + genson (libreria di database di java / json). Devi solo rilasciare genson + jersey nel tuo percorso di classe e json è automaticamente supportato.

MODIFICARE:

  • La soluzione 2 è la più difficile da implementare, ma il vantaggio è che puoi gestire bene le eccezioni e non solo gli errori aziendali, ma lo sforzo iniziale è più importante ma vinci a lungo termine.

  • La soluzione 3 è facile da implementare sia sul lato server che sul client, ma non è così bello in quanto dovrai incapsulare gli oggetti che vuoi restituire in un oggetto risposta contenente anche l'errore responseValid +.


2
Tu dici che dovrei "Definire una struttura uniforme per gli errori" e altri suggerimenti simili, ma questo è esattamente quello che sto chiedendo. Immagino che la risposta si stia rivelando che "no, non esistono standard o migliori pratiche riguardo a questa struttura".
FtDRbwLXw6

7
Per la cronaca: il codice di stato HTTP non è un'intestazione.
pepkin88,

3
"la risposta non sarà json ma html." sbagliato! html non ha nulla a che fare con la gestione degli errori. la risposta può essere qualsiasi tipo di contenuto supportato.
Oligofren,

2
@ ア レ ッ ク ス Il codice di stato HTTP è un codice di 3 cifre nella riga di stato dell'intestazione di una risposta HTTP. Seguendo quella linea ci sono campi di intestazione, colloquialmente chiamati anche intestazioni.
pepkin88,

1
@ ア レ ッ ク ス La pagina di Wikipedia su HTTP risponde bene alle tue domande, puoi controllarlo lì: en.wikipedia.org/wiki/… (link alla sezione Messaggio di risposta)
pepkin88


19

Di seguito è il formato json che instagram sta usando

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

19

Non sarò così arrogante nel sostenere che si tratta di uno standard, quindi userò il modulo "Preferisco".

Preferisco una risposta concisa (quando richiedo un elenco di / articoli voglio un array di articoli JSON).

Nei miei progetti utilizzo HTTP per il rapporto sullo stato, un 200 restituisce solo il payload.

400 restituisce un messaggio di ciò che era sbagliato nella richiesta:

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

Ritorno 404 se il modello / controler / URI non esiste

Se si è verificato un errore durante l'elaborazione da parte mia, restituisco 501 con un messaggio:

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

Da quello che ho visto alcuni framework REST tendono a seguire questa linea.

Fondamento logico :

JSON dovrebbe essere un formato di payload , non è un protocollo di sessione. L'intera idea di payload dettagliati di sessione proviene dal mondo XML / SOAP e da varie scelte sbagliate che hanno creato quei disegni gonfiati. Dopo che ci siamo resi conto che si trattava di un forte mal di testa, il punto cruciale di REST / JSON era di KISS e aderire a HTTP. Non credo che ci sia qualcosa di lontanamente di serie in entrambi i JSend e soprattutto non con il più dettagliato tra di loro. XHR reagirà alla risposta HTTP, se si utilizza jQuery per AJAX (come la maggior parte fa), è possibile utilizzare try/ catche done()/ fail()callback per acquisire errori. Non riesco a vedere come l'incapsulamento dei rapporti sullo stato in JSON sia più utile di così.


2
"JSON è un formato di payload ...". No, JSON è un formato di serializzazione dei dati. Puoi usarlo per trasmettere tutto quello che vuoi, inclusi i protocolli di sessione o solo semplici payload. I tuoi commenti KISS sono comunque mirati e indipendenti da JSON. Meglio mantenere il JSON concentrato su ciò che è (dati di successo o dati di motivazione del fallimento come descrivi) piuttosto che inquinarlo con un po 'di miscuglio di entrambi che deve essere costantemente composto e successivamente eliminato. Quindi puoi andare fino in fondo e archiviare i tuoi dati JSON come sono in Couchbase e restituirli come sono all'applicazione.
Dirk Bester,

1
Forse avrei dovuto formularlo come "dovrebbe essere un formato di payload", ma a parte questo, resto in attesa del mio commento. È possibile inserire i dati di sessione / errore come attributi del tag body nel documento HTML, ma ciò non lo rende il modo giusto o ragionevole per farlo.
Bojan Markovic,

16

Per quello che vale, lo faccio diversamente. Una chiamata riuscita ha solo gli oggetti JSON. Non ho bisogno di un oggetto JSON di livello superiore che contenga un campo di successo che indica true e un campo payload con l'oggetto JSON. Restituisco l'oggetto JSON appropriato con un 200 o qualsiasi cosa sia appropriata nell'intervallo 200 per lo stato HTTP nell'intestazione.

Tuttavia, se c'è un errore (qualcosa nella famiglia 400) restituisco un oggetto errore JSON ben formato. Ad esempio, se il client sta postando un utente con un indirizzo e-mail e un numero di telefono e uno di questi è malformato (cioè non riesco a inserirlo nel mio database sottostante) restituirò qualcosa del genere:

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

I bit importanti qui sono che la proprietà "field" deve corrispondere esattamente al campo JSON che non può essere validato. Ciò consente ai clienti di sapere esattamente cosa è andato storto nella loro richiesta. Inoltre, "messaggio" si trova nelle impostazioni internazionali della richiesta. Se sia "emailAddress" che "phoneNumber" non fossero validi, l'array "errori" conterrebbe voci per entrambi. Un corpo di risposta JSON 409 (conflitto) potrebbe essere simile al seguente:

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

Con il codice di stato HTTP e questo JSON il client ha tutto ciò di cui ha bisogno per rispondere agli errori in modo deterministico e non crea un nuovo standard di errore che tenta di completare la sostituzione dei codici di stato HTTP. Nota, questi si verificano solo per l'intervallo di 400 errori. Per qualsiasi cosa nella gamma 200 posso semplicemente restituire ciò che è appropriato. Per me è spesso un oggetto JSON simile a HAL ma qui non importa davvero.

L'unica cosa che ho pensato di aggiungere era un codice numerico di errore nelle voci dell'array "errori" o nella radice dell'oggetto JSON stesso. Ma finora non ne abbiamo avuto bisogno.


9

Il loro non è d'accordo sui restanti formati di risposta API dei grandi giganti del software - Google, Facebook, Twitter, Amazon e altri, sebbene molti collegamenti siano stati forniti nelle risposte sopra, in cui alcune persone hanno cercato di standardizzare il formato di risposta.

Poiché le esigenze delle API possono variare, è molto difficile coinvolgere tutti e concordare un determinato formato. Se hai milioni di utenti che utilizzano la tua API, perché dovresti cambiare il formato di risposta?

Di seguito è la mia opinione sul formato di risposta ispirato a Google, Twitter, Amazon e alcuni post su Internet:

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

File Swagger:

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


1
votazione per il formato rest-api-response-free senza buste
Kerem Baydoğan,

@adnan kamilli - >>> StatusCode: 304, ReasonPhrase: 'Not Modified', Versione: 1.1, Contenuto: <null>, Headers: {} <<<< è una risposta adeguata di restApi?
Arnold Brown,

@ArnoldBrown Per quale endpoint API: azione stai restituendo questo codice?
adnan kamili,

è un ritorno di risposta di un API utilizzato per caricare un'immagine (dati del modulo) - Cliente scritto API.
Arnold Brown,

7

Il punto di JSON è che è completamente dinamico e flessibile. Piegalo a qualsiasi capriccio che desideri, perché è solo un insieme di oggetti e array JavaScript serializzati, radicati in un singolo nodo.

Quale sia il tipo di rootnode dipende da te, cosa contiene dipende da te, se invii metadati insieme alla risposta dipende da te, se imposti il ​​tipo mime application/jsono lo lasci come text/plaindipende da te ( purché tu sappia come gestire i casi limite).

Crea uno schema leggero che ti piace.
Personalmente, ho scoperto che il tracciamento analitico e il servizio mp3 / ogg e il servizio di gallerie di immagini e messaggi di testo e pacchetti di rete per giochi online, e post di blog e commenti di blog hanno tutti requisiti molto diversi in termini di ciò che è inviato e cosa viene ricevuto e come dovrebbero essere consumati.

Quindi l'ultima cosa che vorrei fare, quando faccio tutto questo, è provare a rendere ognuno conforme allo stesso standard di plateplate, che si basa su XML2.0 o somesuch.

Detto questo, c'è molto da dire sull'uso di schemi che hanno senso per te e sono ben pensati.
Basta leggere alcune risposte API, annotare ciò che ti piace, criticare ciò che non ti piace, scrivere quelle critiche e capire perché ti strofinano nel modo sbagliato, quindi pensa a come applicare ciò che hai imparato a ciò di cui hai bisogno.


1
Grazie per la risposta, ma ancora una volta non sono preoccupato per i payload stessi. Mentre i tuoi esempi hanno tutti requisiti molto diversi in termini di ciò che viene inviato / ricevuto all'interno dei payload e di come questi payload vengono consumati, devono tutti risolvere gli stessi problemi rispetto alla risposta stessa . Vale a dire, devono tutti determinare se la richiesta ha avuto esito positivo. In tal caso, procedere con l'elaborazione. In caso contrario, cosa è andato storto. È questa caldaia che è comune a tutte le risposte API a cui mi riferisco nella mia domanda.
FtDRbwLXw6

Restituisci uno stato di 200 per tutto e definisci un payload di errore specifico oppure restituisci uno stato commisurato all'errore, con e / o senza ulteriori dettagli nel corpo del payload (se supportato). Come ho detto, lo schema dipende da te, comprese le informazioni relative a meta / stato. È un'ardesia vuota al 100% a che fare con ciò che ti piace in base al tuo stile di architettura preferito.
Norguard,

2
Mi rendo conto che è una tabula rasa da fare a mio piacimento. Lo scopo della mia domanda è di chiedere se esistessero standard emergenti per quanto riguarda la struttura. Non stavo chiedendo "cos'è JSON e come lo uso", ma piuttosto "so come usare JSON per restituire / strutturare tutto ciò che voglio, ma mi piacerebbe sapere se ci sono strutture standard utilizzate o diventando popolare ". Mi dispiace se ho sbagliato a scrivere una domanda. Grazie per la tua risposta, comunque.
FtDRbwLXw6,

7

JSON-RPC 2.0 definisce un formato di richiesta e risposta standard ed è una boccata d'aria fresca dopo aver lavorato con le API REST.


L'unica cosa che JSON-RPC_2.0 offre per le eccezioni è un codice di errore? Un codice di errore numerico non può rappresentare con fedeltà il problema che si è verificato.
AgilePro

@AgilePro D'accordo, un codice di errore numerico non è molto carino e vorrei che gli autori delle specifiche avessero permesso al codecampo di essere una stringa. Fortunatamente le specifiche ci consentono di inserire qualsiasi informazione desideriamo nel campo dell'errore data. Nei miei progetti JSON-RPC di solito utilizzo un unico codice numerico per tutti gli errori a livello di applicazione (al contrario di uno degli errori del protocollo standard). Quindi ho inserito le informazioni dettagliate sull'errore (incluso un codice stringa che indica il tipo di errore reale) nel datacampo.
dnault

2

Per quelli che verranno dopo, oltre alla risposta accettata che include HAL, JSend e JSON API, aggiungerei alcune altre specifiche che vale la pena esaminare:

  • JSON-LD , che è una raccomandazione W3C e specifica come creare servizi Web interoperabili in JSON
  • Ion Hypermedia Type for REST, che si afferma come "un tipo hypermedia JSON semplice e intuitivo per REST"

1

Il framework di base suggerito sembra a posto, ma l'oggetto errore come definito è troppo limitato. Spesso non è possibile utilizzare un singolo valore per esprimere il problema, e invece è necessaria una catena di problemi e cause .

Ho fatto una piccola ricerca e ho scoperto che il formato più comune per la restituzione dell'errore (eccezioni) è una struttura di questo modulo:

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

Questo è il formato proposto dallo standard di dati OASIS OASIS OData e sembra essere l'opzione più standard là fuori, tuttavia a questo punto non sembrano esserci alti tassi di adozione di alcuno standard. Questo formato è coerente con le specifiche JSON-RPC.

Puoi trovare la libreria open source completa che lo implementa su: Mendocino JSON Utilities . Questa libreria supporta gli oggetti JSON e le eccezioni.

I dettagli sono discussi nel mio post sul blog sulla gestione degli errori nell'API REST JSON


0

Non esiste uno standard di violazione della legge o fuorilegge diverso dal buon senso. Se astraggiamo questo come due persone che parlano, lo standard è il modo migliore in cui possono capirsi con precisione in parole minime in un tempo minimo. Nel nostro caso, "parole minime" sta ottimizzando la larghezza di banda per l'efficienza del trasporto e "comprendere accuratamente" è la struttura per l'efficienza del parser; che alla fine finisce con meno dati e comune la struttura; in modo che possa passare attraverso un foro e può essere analizzato attraverso un ambito comune (almeno inizialmente).

Quasi in tutti i casi suggeriti, vedo risposte separate per lo scenario "Successo" e "Errore", il che è una specie di ambiguità per me. Se le risposte sono diverse in questi due casi, allora perché abbiamo davvero bisogno di mettere un flag "Successo" lì? Non è ovvio che l'assenza di "Errore" è un "Successo"? È possibile avere una risposta in cui "Successo" è VERO con un "Errore" impostato? Oppure, "Successo" è FALSO senza "Errore" impostato? Una sola bandiera non è abbastanza? Preferirei avere solo il flag "Errore", perché credo che ci sarà meno "Errore" rispetto a "Successo".

Inoltre, dovremmo davvero rendere "Errore" una bandiera? E se volessi rispondere con più errori di validazione? Quindi, trovo più efficiente avere un nodo 'Errore' con ogni errore come figlio di quel nodo; dove un nodo "Errore" vuoto (conta fino a zero) indicherebbe un "Successo".


-2

Migliore risposta per le API Web che possono essere facilmente comprese dagli sviluppatori di dispositivi mobili.

Questo è per la risposta "Success"

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

Questo è per la risposta "Errore"

{
    "ReturnCode": "4",
    "ReturnMsg": "Invalid Username and Password",
    "ReturnValue": "",
    "Data": {}
}

2
Sarebbe meglio standardizzare le tue proprietà. Sono tutti valori "Return ...". Ma i dati non hanno il prefisso. Direi di eliminare tutti i prefissi "Return".
z0mbi3

Includere anche "Return" è piuttosto ridondante.
Jack Marchetti l'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.