API RESTful representa a ausência de algo

18

Imagine uma API para identificar se uma pessoa selecionou seu animal espiritual. Eles só podem ter zero ou um animal espiritual.

Atualmente:

/person/{id}/selectedSpiritAnimal

quando eles selecionam um animal retorna http 200 e {selectedAnimal:mole}

mas quando eles não têm seleção, ele retorna http 404.

Isso deixa meu animal espiritual infeliz, pois estamos representando uma preocupação válida de domínio - ainda não tendo selecionado um animal espiritual - como um erro HTTP.

Além disso, como empresa - erm Sprit-Animal-Hampers-R-us - queremos saber quando alguém não tem seleção para que possamos solicitá-la.

Qual é a melhor resposta aqui:

HTTP 200 e {selectedAnimal:null}

ou ainda mais explícito

HTTP 200 e {selectedAnimal:null, spiritAnimalSelected: false}

Ou é melhor retornar um 404? Já que muito parecido this image has not yet been uploadedao visualizar uma imagem on-line seria um 404. this person has not selected a spirit animalpode ser um 404


Esta pergunta foi proposta como duplicada, mas trata de uma URL válida, caso contrário, sendo solicitada quando o aplicativo foi configurado para não permitir a alteração que a URL representa.

Considerando que aqui eu estou olhando como alguém representa um recurso em que a ausência do recurso é significativa. Ou seja, é válido que o cliente solicite a URL e a resposta é que você solicitou com êxito o recurso que representa a ausência de algo.

Portanto, isso não é 'lógica de negócios', mas uma circunstância em que a ausência de algo tem significado (pode ser que muitos de meus colegas estejam argumentando que 404 ainda está correto), mas não tenho certeza de como mapear isso para o spec.


Muito difícil escolher uma resposta. Mudei de idéia várias vezes ao longo da conversa aqui e da que está em andamento no trabalho.

O que resolve isso aqui para mim é que a especificação diz que 4xx é quando o cliente cometeu um erro . Nesse caso, o cliente foi instruído a esperar uma resposta do URL selectedSpiritAnimal, para que não tenha errado.

O consenso entre meus colegas é de que isso é um sintoma de um mau design de API

Provavelmente seria melhor simplesmente solicitar / person / {id} e retornar um conjunto de relações de link para a pessoa ... se você não receber o link / selectedSpiritAnimal (quando uma pessoa não tiver seleção), mas você chame assim mesmo, então um 404 faz sentido. Ou que você implemente respostas parciais e deixe / person / {id} retornar um documento mais completo, a menos que o cliente solicite um subconjunto dos dados

Paul D'Ambra
fonte
5
Você não explicou por que acredita que é um problema retornar um 404. Do ponto de vista do domínio, você não sabe se recebeu 404 ou 200, abstraído pela camada do cliente.
Vincent Savard 25/09
14
ou talvez seja melhor retornar 204 sem conteúdo?
Paul D'Ambra
6
Suponho que a minha preocupação com a 404 é disambiguating uma mudança de configuração que introduz um modelo ruim URL de uma pessoa com nenhum animal espírito
Paul D'Ambra
2
@ PaulD'Ambra Por que vale a pena, eu não usaria nenhum conteúdo. Não vejo códigos HTTP manipulados no front-end dessa maneira em Javascript desde os dias XmlHttpRequest. Mas é certamente válido.
Brandon Arnold

Respostas:

24

Os códigos HTTP 4xx provavelmente não são a escolha certa para esse cenário. Você declara que ter animais com espírito zero é um estado válido, e a rota da API person/{id}/selectedSpiritAnimalexplica se a pessoa idpossui ou não um.

As respostas HTTP 4xx são reservadas para a situação em que um cliente fez algo incorreto na solicitação (consulte o arquivo w3 da especificação original ). Mas o cliente está fazendo uma solicitação válida, independentemente de a pessoa idter ou não um animal espiritual.

Então, eu me inclino para as segundas soluções usando um corpo JSON formatado corretamente na resposta e um código HTTP 2xx.

Agora, se você receber uma solicitação desse tipo e a pessoa idnão existir, um código 4xx fará mais sentido.

Brandon Arnold
fonte
17
"As respostas HTTP 4xx são reservadas para a situação em que um cliente fez algo incorreto na solicitação". Ao fazer declarações autorizadas sobre a especificação, faça referência à especificação HTTP real e não (a) uma estrutura construída sobre ela ou (b) uma postagem aleatória no blog.
Eric Stein
5
@ EricStein, eu me senti um pouco preguiçoso com isso; obrigado pela crítica. Atualizada.
Brandon Arnold
1
Eu sinto que o link RFC aqui é meio conflitante. Por um lado, a porção 4xx diz cases in which the client seems to have erred, mas, por outro, o 404 diz diretamente The server has not found anything matching the Request-URI. Você pode solicitar algo que não exista um erro do cliente. Entendo que a pessoa não existe vs um subprop não existe, mas isso pode ser indicado como mensagem de resposta. 204 também parece relevante, uma vez que a entidade existe, mas não possui conteúdo para uma subpropriedade.
shortstuffsushi
1
O cliente fez algo errado; solicitou o animal espiritual selecionado para um usuário quando ele não existe. Isso é exatamente o que 404 significa ... você pediu algo que não existe.
Andy
5
Quando meu navegador faz uma solicitação para softwareengineering.stackexchange.com/questions/unicorn , é uma solicitação válida e ainda assim recebo um 404 (acontece que não há dúvida sobre o ID "unicorn").
user253751
24

Deixe-me apresentar-lhe o Modelo de Maturidade de Richardson .

Seu problema é que você está representando dois recursos como um, onde realmente deve ter dois recursos que tenham um relacionamento indicado pela hipermídia. Usar a hipermídia para descrever relacionamentos é o glorioso nível 3 do Descanso.

Sua pessoa deve viver sob o URI /person/{id}e o animal deve viver sob /spiritanimal/{id}. A pessoa deve indicar que possui um espírito animal usando um link para o animal.

Vamos imaginar uma pessoa chamada Bob, que tem id 123 e um animal espiritual de unicórnio.

GET /person/123

retornaria;

{
  "name": "Bob",
  "links": [
    {
      "rel": "spiritanimal",
      "uri": "/spiritanimals/789"
    }
  ]
}

Agora, quem lê a pessoa 123 saberá que tem um animal espiritual e possui o URI onde poderá obter mais informações.

GET /spitiranimal/789

pode retornar

{
  "type": "Unicorn"
}

Agora vamos imaginar uma pessoa chamada Fred, que tem o ID 456 e nenhum animal espiritual.

GET /person/456

retornaria;

{
  "name": "Fred",
  "links": [
  ]
}

Agora, quem lê a pessoa 456 saberá que não tem animal espiritual, pois não há vínculo. Não há necessidade de usar nenhum código de status HTTP para representar a falta de um relacionamento.

Qwerky
fonte
1
Foi apenas acrescentando à pergunta que, dada a capacidade de alterar a API, algum nível de HATEOAS provavelmente seria a solução certa. Esse é um ótimo exemplo disso
Paul D'Ambra
1

Este é o URL apropriado para obter animais espirituais; portanto, um erro 404 é inadequado. 404 é para representar um problema técnico, não um problema de lógica.

A solução apropriada é retornar http 200 e {"selectedAnimal": null}

Você deve ter um método da web separado/person/{id}/hasSelectedSpiritAnimal que retorne {"isSpiritAnimalSelected": false}. Nos bastidores, ele pode ou não fazer as mesmas chamadas de método, retornando false se nulo, mas cabe a ele decidir, não o código consumidor.

É melhor evitar a combinação de consultas separadas em um método da Web sem um motivo convincente para fazê-lo, mesmo se as consultas estiverem intimamente relacionadas.

TheCatWhisperer
fonte
1
Você poderia explicar por que acredita que esta é a solução apropriada? Não consigo fazer sentido de retornar dados quando não há dados a serem retornados. Concordo com você que um 404 não faz sentido, pois a URL está correta, portanto, um 204 parece estar fazendo mais sentido para mim.
Vincent Savard 25/09
2
Os códigos de status @VincentSavard são mais difíceis de trabalhar do que as respostas json e são mais apropriados para problemas técnicos, em vez de comunicar informações de domínio.
TheCatWhisperer
2
204: Nenhum conteúdo com um corpo vazio. É tudo claro e auto-explicativo. Não há necessidade de discutir mais. Em suma, quando não há um padrão de design claro, um SE deve escolher um, dobrá-lo de acordo com sua necessidade e fornecer documentação. Devolver um corpo com uma resposta 204 não é conseqüente.
formixian
2
@formixian ... Se você estivesse criando uma API bjson / tcp em vez de uma json / http, o que você retornaria para este caso? Confiar em códigos http parece-me vincular desnecessariamente os resultados da API à sua implementação http.
TheCatWhisperer
2
@VincentSavard When you said "the spirit animal is not currently on file", it seemed quite similar to me to "there is no content"Nenhum conteúdo significa nenhum conteúdo . ou seja: retorna "". Esses dois não são parecidos. "o animal espiritual não está no arquivo" é muito diferente de "".
Shane
1

O que seu endpoint representa não é apenas um animal; é um animal ou falta dele. É um valor melhor representado por umOptional / Maybe/ Nullable/ etc.

Portanto, valores legítimos (como em 200 OK) podem ser:

  • {'animal': <some animal>, 'selected': true}
  • {'animal': null, 'selected': false}

Eu poderia imaginar isso DELETE método, quando aplicado ao ponto de extremidade, pode definir 'selected'a falsenovamente, isto é, retirar a animal seleccionado.

Você pode, é claro, soltar a 'selected'chave aqui, ela é mostrada apenas para maior clareza; string vs nullé suficiente para a distinção.

9000
fonte
0

Você deve usar 404.

O código de status 404 (não encontrado) indica que o servidor de origem não encontrou uma representação atual para o recurso de destino

RFC7231

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

"mole"

HTTP/1.1 404 Not Found
Content-Type: text/plain
Date: Mon, 25 Sep 2017 00:00:00 GMT

this person has not selected a spirit animal

Como você está criando uma interface de programação, não uma interface humana, o texto 404 é opcional.

Eu prefiro isso porque prefiro protocolos padrão, tanto quanto possível. O HTTP tem uma maneira de representar a inexistência, e é isso que eu usaria.

EDIT: Suponha que você tenha adicionado um recurso em que os usuários possam escolher se desejam compartilhar seus animais espirituais e alguém não o compartilhar. Você retornaria 200 OK nullou 200 OK "Unauthorized? Ou você usaria o padrão 401 Não Autorizado / 403 Proibido? Isso parece diretamente análogo a não escolher um em primeiro lugar.


Como alternativa, se você quiser usar 200 OK + JSON, retorne null.

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

"mole"

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

null

Mantenha as coisas limpas. Crie mais quebra automática apenas se necessário.

Paul Draper
fonte
2
Os dados são encontrados, no entanto. Acontece que os dados são null(não têm valor). Isso é diferente do cliente que solicita algo para o qual não existe slot para armazenar o valor. Você não sugere jogar um AttributeErrorem Python quando o valor é None; isso tornaria a interface impraticável e desnecessariamente difícil de trabalhar. Mais pragmaticamente, muitas APIs de clientes HTTP podem converter o 404 no mecanismo de tratamento de erros padrão do idioma (código de erro, exceção, tipo de erro), o que significa que você precisará suprimir um erro estranho.
precisa saber é o seguinte
@Paul Draper Role um pouco as especificações, você verá que ele faz uma declaração geral sobre o HTTP 4xx: "A classe 4xx do código de status é destinada aos casos em que o cliente parece estar errado." O op declarou claramente que não ter um animal espiritual é um estado válido pelo qual a API deve ser considerada. tools.ietf.org/html/rfc7231#section-6.5
Brandon Arnold
Como você, então, distingue entre indicar a inexistência do recurso solicitado (ou seja, nenhum animal selecionado) e o cliente solicitando um caminho inválido (ou seja /person/{id}/selectedSpritAnimal, observe o erro de digitação Sprit).
Sampathsris 26/09/17
1
Independentemente da intenção, um 404 significa estritamente que o recurso não foi encontrado. Se um selectedSpiritAnimal é um recurso e não existe, então não há nada para GET e um 404 é apropriado. No entanto, se o cliente quiser saber se um animal espiritual foi selecionado, o servidor deverá expor outro recurso /person/{id}/isSpiritAnimalSelectedque indique ao cliente se um foi selecionado. Parece-me o mesmo exemplo clássico de testar se existe um arquivo antes de lê-lo. Basta ler o arquivo e manipular o erro ENOENT, caso seja lançado.
aaaaaa 26/09
1
@PaulDraper A questão não é se o recurso existe ou não. O ponto é que os códigos HTTP 4xx se destinam a quando o chamador está usando a API incorretamente. Op afirma que /person/{id}/selectedSpiritAnimaldeve ser usado mesmo que o animal espiritual não exista. Isso significa que o HTTP 4xx está incorreto, quando usado nesse caso. Op também afirma corretamente que isso pode ser um cheiro de mau design da API.
Brandon Arnold