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


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.


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.


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.


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.


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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.