Formato de resposta da API JSON padrão?

697

Existem padrões ou práticas recomendadas para estruturar respostas JSON a partir de uma API? Obviamente, os dados de cada aplicativo são diferentes, de modo que não estou preocupado com isso, mas com o "modelo de resposta", se preferir. Um exemplo do que quero dizer:

Pedido bem sucedido:

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

Pedido com falha:

{
  "success": false,
  "payload": {
    /* Application-specific data would go here. */
  },
  "error": {
    "code": 123,
    "message": "An error occurred!"
  }
}
FtDRbwLXw6
fonte
16
As pessoas provavelmente ter aprendido com SOAP e não vai construí-lo de novo ...
Denys Séguret
18
@dystroy: Gostaria de explicar o seu comentário?
FtDRbwLXw6 9/10/12
5
Fiquei realmente interessado nessa questão, pois tive que criar uma API JSON recentemente e me perguntei se havia algum padrão para definir um formato de resposta. O seu realmente parece muito bom e vale a pena usar se você não encontrar um padrão. É uma pena que as respostas fornecidas não abordem a questão.
11808 Alex
13
@ Alex, infelizmente, é porque não importa onde você vá, não há um padrão. Não apenas no próprio JSON, mas em termos de como usá-lo para aplicativos RESTful ou qualquer outra coisa do tipo. Todo mundo faz diferente. Você pode se sentir livre para seguir as práticas recomendadas (respostas HTTP, estrutura de pacotes significativa, um olhar para estruturar seus dados para consumo pelo seu sistema), mas todos que são grandes distribuidores estão fazendo pelo menos uma coisa diferente das outras. .. Não existe um padrão, e provavelmente não haverá um, então crie algo sólido e faça-o para você.
Norguard 12/10
5
@ Norguard existem padrões (veja minha resposta). De fato, o bom dos padrões é que você tem muitos por onde escolher. - Andrew Tanenbaum
Adam Gent

Respostas:

640

Sim, existem alguns padrões (embora algumas liberdades na definição de padrão) surgiram:

  1. API JSON - A API JSON também abrange a criação e atualização de recursos, não apenas respostas.
  2. JSend - Simples e provavelmente o que você já está fazendo.
  3. Protocolo JSata OData - Muito complicado.
  4. HAL - Como OData, mas com o objetivo de ser HATEOAS .

Existem também formatos de descrição da API JSON:

Adam Gent
fonte
19
Obrigado. JSend em particular é exatamente o que eu estava procurando. É semelhante ao que eu estava fazendo, mas tem alguns benefícios que meu método não teve. Para ser justo com @trungly, JSend também está muito próximo de sua própria resposta.
precisa saber é o seguinte
8
Para respostas de erro específicas, também gosto do rascunho RFC de Detalhes do problema para APIs HTTP .
Pieter Ennes
1
Talvez você queira adicionar code.google.com/p/json-service à lista de formatos de descrição?
emilesilvis
1
Eu acho que o rótulo "Um padrão recomendado para o Rails" é um exagero - essa é apenas a solução de um programador. Não sabe o que o torna um "padrão recomendado" (especialmente se você observar a popularidade da gema - não parece que muitas pessoas estão usando isso)? Eu, pessoalmente, não acho que a maioria dos programadores Rails recomendaria esta solução por causa de usar corpo da resposta em vez de cabeçalhos HTTP para o estado
Iwo Dziechciarow
2
O Guia de Estilo JSON do Google também é uma boa referência
MRodrigues
195

Guia JSON do Google

Retorno de resposta de sucesso data

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

Retorno de resposta a erro error

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

e se seu cliente for JS, você poderá if ("error" in response) {}verificar se há um erro.

Asa de aço
fonte
13
Primeiro de tudo, o guia JSON do Google recomenda o uso de aspas duplas em vez de aspas simples.
rpozarickij
1
Não tenho certeza se você pode lidar com isso a partir de uma API JSON do lado do servidor como o PlayJson, de qualquer forma, não importa. @Steely seus links estão quebrados
Rhys Bradbury
3
E os erros que precisam fornecer uma lista de falhas (como problemas de validação)?
Xeoncross
1
@Xeoncross clique no link da palavra error, a página do Google dá um exemplo disso
MI Wright
@Xeoncross Você pode retornar uma lista de falhas usando error.errors [], definido como: "Contêiner para obter informações adicionais sobre o erro. Se o serviço retornar vários erros, cada elemento na matriz de erros representará um erro diferente." Talvez o erro de nível superior mencione "Solicitação com falha na validação de entrada" e a matriz de erros [] tenha uma entrada para cada falha de validação específica que ocorreu.
James Daily
130

Eu acho que um padrão defacto realmente não surgiu (e pode nunca). Mas, independentemente, aqui está a minha opinião:

Pedido bem sucedido:

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

Pedido com falha:

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

Vantagem: Os mesmos elementos de nível superior nos casos de sucesso e erro

Desvantagem: nenhum código de erro, mas se você quiser, pode alterar o status para ser um código (de êxito ou falha) ou pode adicionar outro item de nível superior chamado "código".

trungly
fonte
3
sim, este é o caminho certo se você estiver usando o POJO para a análise de json! Quando estamos usando POJOs, precisamos de um formato json estático e não dinâmico!
LOG_TAG 10/04
Simples e direto ao ponto. Melhor que jsend na minha opinião, porque jsend distingue erro de falha.
Josue Alexander Ibarra
1
Eu também uso esse padrão, mas com um campo chamado, messagesque é uma matriz de mensagens em vez de uma única string.
295:
4
A resposta é quase uma cópia do JSend bem documentado, que é simples e muito útil. Eles forneceram o terceiro status failpara problemas típicos de validação, enquanto errorsão usados ​​apenas com fatais, como erros de banco de dados.
precisa saber é
para o sucesso: se tem 200nos cabeçalhos, por que você precisa de um statuscampo? basta retornar o objeto de dados diretamente. Você sabe que isso pode causar problemas adicionais em linguagens FE digitadas como o TypeScript.
Denise M. #
84

Supondo que sua pergunta seja sobre o design de serviços da Web REST e mais precisamente sobre sucesso / erro.

Eu acho que existem 3 tipos diferentes de design.

  1. Use apenas o código de status HTTP para indicar se houve um erro e tente limitar-se aos códigos padrão (geralmente deve ser suficiente).

    • Prós: É um padrão independente da sua API.
    • Contras: Menos informações sobre o que realmente aconteceu.
  2. Use HTTP Status + json body (mesmo que seja um erro). Defina uma estrutura uniforme para erros (ex: código, mensagem, motivo, tipo, etc) e use-a para erros, se for um sucesso, basta retornar a resposta json esperada.

    • Prós: Ainda padrão, ao usar os códigos de status HTTP existentes e você retorna um json descrevendo o erro (você fornece mais informações sobre o que aconteceu).
    • Contras: A saída json variará dependendo se for um erro ou êxito.
  3. Esqueça o status http (ex: sempre status 200), sempre use json e adicione na raiz da resposta um responseValid booleano e um objeto de erro (código, mensagem, etc.) que será preenchido se houver algum erro, caso contrário, os outros campos (sucesso) são preenchidos.

    • Prós: O cliente lida apenas com o corpo da resposta que é uma string json e ignora o status (?).

    • Contras: O menos padrão.

Cabe a você escolher :)

Dependendo da API, eu escolheria 2 ou 3 (eu prefiro 2 para json rest apis). Outra coisa que experimentei ao projetar a API REST é a importância da documentação para cada recurso (url): os parâmetros, o corpo, a resposta, os cabeçalhos etc + exemplos.

Também recomendo que você use jersey (implementação de jax-rs) + genson (biblioteca de ligação de dados java / json). Você só precisa soltar genson + jersey no seu caminho de classe e o json é automaticamente suportado.

EDITAR:

  • A solução 2 é a mais difícil de implementar, mas a vantagem é que você pode lidar bem com exceções e não apenas com erros de negócios; o esforço inicial é mais importante, mas você vence a longo prazo.

  • A solução 3 é fácil de implementar no lado do servidor e no cliente, mas não é tão agradável quanto você terá que encapsular os objetos que deseja retornar em um objeto de resposta que também contenha o erro responseValid +.

eugen
fonte
2
Você diz que eu deveria "Definir uma estrutura uniforme para erros" e outras sugestões semelhantes, mas é exatamente isso que estou perguntando. Acho que a resposta está sendo "não, não há práticas recomendadas ou padrão com relação a essa estrutura".
FtDRbwLXw6 9/10/12
7
Para o registro: o código de status HTTP não é um cabeçalho.
pepkin88
3
"a resposta não será json, mas html." errado! html não tem nada a ver com o tratamento de erros. a resposta pode ser qualquer tipo de conteúdo que você suporte.
Oligofren
2
Código de status HTTP é um código de 3 dígitos na linha de status do cabeçalho de uma resposta HTTP. Após essa linha, existem campos de cabeçalho, coloquialmente também chamados de cabeçalhos.
pepkin88
1
@アレックスA página da Wikipedia sobre HTTP bem responde às suas perguntas, você pode verificá-lo lá: en.wikipedia.org/wiki/... (link para a mensagem secção Response)
pepkin88
19

A seguir está o formato json que o instagram está usando

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

Não serei tão arrogante em afirmar que esse é um padrão, então usarei o formulário "Prefiro".

Prefiro resposta concisa (ao solicitar uma lista de / articles, quero uma matriz de artigos JSON).

Nos meus projetos, eu uso o HTTP para o relatório de status, um 200 retorna apenas a carga útil.

400 retorna uma mensagem do que estava errado com a solicitação:

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

Retorne 404 se o modelo / controlador / URI não existir

Se houver um erro no processamento do meu lado, retornarei 501 com uma mensagem:

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

Pelo que vi, algumas estruturas REST-ish tendem a seguir essa linha.

Fundamentação da petição :

JSON deve ser um formato de carga útil , não é um protocolo de sessão. Toda a idéia de cargas úteis detalhadas de sessão vem do mundo XML / SOAP e de várias opções equivocadas que criaram esses designs inchados. Depois que percebemos que tudo era uma grande dor de cabeça, o ponto principal do REST / JSON era beijá-lo e aderir ao HTTP. Eu não acho que exista algo remotamente padrão no JSend e, especialmente, não entre os mais detalhados entre eles. O XHR reagirá à resposta HTTP, se você usar o jQuery para o seu AJAX (como a maioria faz), poderá usar try/ catche done()/ fail()callbacks para capturar erros. Não vejo como os relatórios de status encapsulantes no JSON são mais úteis do que isso.

Bojan Markovic
fonte
2
"JSON é um formato de carga útil ...". Não, JSON é um formato de serialização de dados. Você pode usá-lo para transmitir o que quiser, incluindo protocolos de sessão ou apenas cargas úteis. Seus comentários no KISS estão no alvo e são independentes do JSON. É melhor manter o JSON focado no que é (dados de sucesso ou dados de razão de falha, como você descreve) do que poluí-lo com alguma confusão de ambos, que constantemente precisa ser composta e posteriormente removida. Depois, você pode percorrer todo o caminho e armazenar seus dados JSON como estão no Couchbase e devolvê-los como estão para o aplicativo.
Dirk Bester
1
Talvez eu devesse formulá-lo como "deveria ser um formato de carga útil", mas, além disso, mantenho meu comentário. Você pode colocar dados de sessão / erro como atributos da tag body no documento HTML, mas isso não torna a maneira correta ou sensata de fazê-lo.
Bojan Markovic 16/04
16

Pelo que vale a pena, faço isso de maneira diferente. Uma chamada bem-sucedida apenas possui os objetos JSON. Não preciso de um objeto JSON de nível superior que contenha um campo de sucesso indicando true e um campo de carga útil que tenha o objeto JSON. Acabo de retornar o objeto JSON apropriado com um 200 ou o que for apropriado no intervalo 200 para o status HTTP no cabeçalho.

No entanto, se houver um erro (algo na família 400), retornarei um objeto de erro JSON bem formado. Por exemplo, se o cliente estiver postando um usuário com um endereço de e-mail e número de telefone e um deles estiver malformado (ou seja, não posso inseri-lo no meu banco de dados subjacente), retornarei algo como isto:

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

Os bits importantes aqui são que a propriedade "field" deve corresponder exatamente ao campo JSON que não pôde ser validado. Isso permite que os clientes saibam exatamente o que deu errado com sua solicitação. Além disso, "message" está no local da solicitação. Se os "emailAddress" e "phoneNumber" forem inválidos, a matriz "errors" conterá entradas para ambos. Um corpo de resposta 409 (Conflict) JSON pode ter a seguinte aparência:

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

Com o código de status HTTP e esse JSON, o cliente tem tudo o que precisa para responder a erros de maneira determinística e não cria um novo padrão de erro que tenta concluir a substituição dos códigos de status HTTP. Observe que isso ocorre apenas no intervalo de 400 erros. Para qualquer coisa na faixa de 200, posso apenas retornar o que for apropriado. Para mim, geralmente é um objeto JSON do tipo HAL, mas isso realmente não importa aqui.

A única coisa que pensei em adicionar foi um código de erro numérico nas entradas da matriz "errors" ou na raiz do próprio objeto JSON. Mas até agora não precisamos disso.

robert_difalco
fonte
9

Eles não concordam com os demais formatos de resposta da API de grandes gigantes de software - Google, Facebook, Twitter, Amazon e outros, embora muitos links tenham sido fornecidos nas respostas acima, onde algumas pessoas tentaram padronizar o formato de resposta.

Como as necessidades das APIs podem diferir, é muito difícil envolver todos e concordar com algum formato. Se você tem milhões de usuários usando sua API, por que alteraria seu formato de resposta?

A seguir, é minha opinião sobre o formato de resposta inspirado no Google, Twitter, Amazon e algumas postagens na Internet:

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

Arquivo Swagger:

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

adnan kamili
fonte
1
upvote para o resto-api-resposta de formato livre de envelope
Kerem Baydoğan
@adnan kamilli - >>> StatusCode: 304, ReasonPhrase: 'Not Modified', Versão: 1.1, Conteúdo: <null>, Headers: {} <<<< essa é uma resposta adequada de restApi?
Arnold Brown
@ArnoldBrown Para qual ponto de extremidade da API - ação você está retornando esse código?
adnan kamili
é um retorno de resposta de uma API usada para carregar uma imagem (dados do formulário) - APIs gravadas pelo cliente.
Arnold Brown
7

O ponto do JSON é que ele é completamente dinâmico e flexível. Dobre-o como quiser, porque é apenas um conjunto de objetos e matrizes JavaScript serializados, enraizados em um único nó.

Qual é o tipo do nó raiz, depende de você, se você envia os metadados junto com a resposta, depende de você definir o tipo MIME application/jsonou deixá-lo como text/plainé seu ( desde que você saiba como lidar com os casos extremos).

Crie um esquema leve que você gosta.
Pessoalmente, eu descobri que a análise de rastreamento e mp3 / ogg servir e imagem-gallery servir e de mensagens de texto e de rede-pacotes para jogos online e de blog-posts e blogue-comenta todos têm requisitos muito diferentes em termos do que é enviado e o que é recebido e como eles devem ser consumidos.

Portanto, a última coisa que eu gostaria, ao fazer tudo isso, é tentar fazer com que cada um esteja em conformidade com o mesmo padrão padrão, que é baseado no XML2.0 ou algo assim.

Dito isto, há muito a ser dito sobre o uso de esquemas que fazem sentido para você e são bem pensados.
Basta ler algumas respostas da API, anotar o que você gosta, criticar o que não gosta, anotar essas críticas e entender por que elas o atrapalham da maneira errada e depois pensar em como aplicar o que aprendeu ao que você precisa.

Norguard
fonte
1
Obrigado pela resposta, mas, novamente, não estou preocupado com as cargas úteis. Embora todos os seus exemplos tenham requisitos muito diferentes em termos do que é enviado / recebido nas cargas úteis e como essas cargas são consumidas, todos eles precisam resolver os mesmos problemas com relação à própria resposta . Ou seja, todos eles precisam determinar se a solicitação foi bem-sucedida. Se foi, continue com o processamento. Se não, o que deu errado. É esse padrão comum a todas as respostas da API às quais estou me referindo na minha pergunta.
FtDRbwLXw6 9/10/12
Retorne um status de 200 para tudo e defina uma carga útil de erro específica ou retorne um status proporcional ao erro, com e / ou sem mais detalhes no corpo da carga útil (se suportado). Como eu disse, o esquema é com você - incluindo qualquer informação de meta / status. É uma lista em branco de 100% a ver com o que você deseja, com base no seu estilo preferido de arquitetura.
Norguard 9/10/12
2
Eu percebo que é uma lousa em branco para fazer o que eu quiser. O objetivo da minha pergunta é perguntar se havia algum padrão emergente no que diz respeito à estrutura. Eu não estava perguntando "o que é JSON e como usá-lo", mas sim "sei como usar o JSON para retornar / estruturar o que eu quiser, mas gostaria de saber se existem estruturas padrão em uso ou se tornando popular ". Sinto muito por ter digitado incorretamente a pergunta. Obrigado pela sua resposta, de qualquer maneira.
FtDRbwLXw6
7

O JSON-RPC 2.0 define um formato padrão de solicitação e resposta e é uma lufada de ar fresco depois de trabalhar com APIs REST.

dnault
fonte
A única coisa que JSON-RPC_2.0 oferece para exceções é um código de erro? Um código de erro numérico não pode representar com nenhuma fidelidade o problema que ocorreu.
AgilePro
@AgilePro Concordo, um código de erro numérico não é muito bom, e eu gostaria que os autores das especificações tivessem permitido que o codecampo fosse uma String. Felizmente, a especificação nos permite inserir as informações que queremos no datacampo do erro . Nos meus projetos JSON-RPC, normalmente uso um código numérico único para todos os erros da camada de aplicativo (em oposição a um dos erros de protocolo padrão). Em seguida, coloquei as informações detalhadas do erro (incluindo um código de sequência indicando o tipo de erro real) no datacampo.
dnault
2

Para aqueles que virão mais tarde, além da resposta aceita que inclui a API HAL, JSend e JSON, eu adicionaria algumas outras especificações que vale a pena examinar:

  • JSON-LD , que é uma recomendação do W3C e especifica como criar serviços da Web interoperáveis ​​em JSON
  • Ion Hypermedia Type for REST, que se autodenomina "um tipo simples e intuitivo de hipermídia baseado em JSON para REST"
A. Barakat
fonte
1

A estrutura básica sugerida parece bem, mas o objeto de erro conforme definido é muito limitado. Geralmente, não se pode usar um único valor para expressar o problema e, em vez disso, é necessária uma cadeia de problemas e causas .

Eu fiz uma pequena pesquisa e descobri que o formato mais comum para retornar erros (exceções) é uma estrutura deste formulário:

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

Esse é o formato proposto pelo padrão de dados OASIS Oata OData e parece ser a opção mais padrão existente no mercado, no entanto, neste momento, não parece haver altas taxas de adoção de nenhum padrão. Este formato é consistente com a especificação JSON-RPC.

Você pode encontrar a biblioteca de código aberto completa que implementa isso em: Mendocino JSON Utilities . Esta biblioteca suporta os objetos JSON, bem como as exceções.

Os detalhes são discutidos na minha postagem no blog sobre Tratamento de erros na API REST JSON

AgilePro
fonte
0

Não existe outro padrão de infracção à lei ou fora da lei que não seja o senso comum. Se abstrairmos isso como duas pessoas conversando, o padrão é a melhor maneira de entenderem-se com precisão em palavras mínimas e em tempo mínimo. No nosso caso, 'palavras mínimas' está otimizando a largura de banda para a eficiência do transporte e 'entendido com precisão' é a estrutura da eficiência do analisador; que acaba por acabar com os menos dados e a estrutura comum; para que ele possa atravessar um orifício e ser analisado através de um escopo comum (pelo menos inicialmente).

Quase todos os casos sugeridos, vejo respostas separadas para os cenários 'Sucesso' e 'Erro', o que é uma ambiguidade para mim. Se as respostas são diferentes nesses dois casos, por que realmente precisamos colocar um sinalizador de 'Sucesso' lá? Não é óbvio que a ausência de 'Erro' seja um 'Sucesso'? É possível ter uma resposta em que 'Sucesso' é VERDADEIRO com um conjunto de 'Erro'? Ou a propósito, 'Sucesso' é FALSO sem 'Erro' definido? Apenas uma bandeira não é suficiente? Eu preferiria ter apenas o sinalizador 'Erro', porque acredito que haverá menos 'Erro' do que 'Sucesso'.

Além disso, devemos realmente fazer do 'Erro' uma bandeira? E se eu quiser responder com vários erros de validação? Portanto, acho mais eficiente ter um nó 'Erro' com cada erro como filho desse nó; onde um nó 'Erro' vazio (conta com zero) denota um 'Sucesso'.

Flecha Quebrada
fonte
-2

Melhor resposta para APIs da Web que podem ser facilmente compreendidas pelos desenvolvedores de dispositivos móveis.

Isto é para resposta "Sucesso"

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

Isto é para resposta "Erro"

{
    "ReturnCode": "4",
    "ReturnMsg": "Invalid Username and Password",
    "ReturnValue": "",
    "Data": {}
}
Manish Vadher
fonte
2
Seria melhor padronizar suas propriedades. Todos são valores "Return ...". Mas Data não é prefixado. Eu diria, largue todos os prefixos "Return".
z0mbi3
Incluir "Return" também é bastante redundante.
Jack Marchetti