L'API RESTful représente l'absence de chose

18

Imaginez une API pour identifier si une personne a sélectionné son animal spirituel. Ils ne peuvent avoir que zéro ou un animal spirituel.

Actuellement:

/person/{id}/selectedSpiritAnimal

quand ils ont sélectionné un animal retourne http 200 et {selectedAnimal:mole}

mais quand ils n'ont aucune sélection, il renvoie http 404.

Cela rend mon animal spirituel malheureux car nous représentons une préoccupation de domaine valide - n'ayant pas encore sélectionné un animal spirituel - comme une erreur HTTP.

De plus, en tant qu'entreprise - erm Sprit-Animal-Hampers-R-us - nous voulons savoir quand quelqu'un n'a pas de sélection afin que nous puissions les inciter.

Quelle est une meilleure réponse ici:

HTTP 200 et {selectedAnimal:null}

ou encore plus explicite

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

Ou vaut-il mieux retourner une 404? Comme un peu comme this image has not yet been uploadedlors de la visualisation d'une image en ligne serait un 404. this person has not selected a spirit animalpourrait être un 404


Cette question a été proposée en tant que doublon, mais elle concerne une URL par ailleurs valide qui est demandée lorsque l'application a été configurée pour ne pas autoriser la modification que l'URL représente.

Alors que je regarde ici comment on représente une ressource où l'absence de la ressource est significative. C'est-à-dire qu'il est valide pour le client de demander l'URL et la réponse est que vous avez réussi à demander la ressource qui représente une absence de chose.

Il ne s'agit donc pas d'une `` logique commerciale '' mais plutôt d'une circonstance où l'absence de chose a un sens (il se peut que beaucoup de mes collègues soutiennent que 404 est toujours correct) mais je ne sais pas comment faire correspondre cela au spec.


Très difficile de choisir une réponse. J'ai changé d'avis plusieurs fois au cours de la conversation ici et de celle en cours au travail.

Ce qui m'arrange ici, c'est que la spécification dit qu'un 4xx, c'est quand le client s'est trompé . Dans ce cas, le client a été invité à attendre une réponse de l'URL selectedSpiritAnimal et n'a donc pas commis d'erreur.

Le consensus parmi mes collègues est que c'est le symptôme d'une mauvaise conception d'API

Il serait probablement préférable que nous demandions simplement / person / {id} et cela renvoie un ensemble de relations de lien pour la personne ... alors si vous ne recevez pas le lien / selectedSpiritAnimal (quand une personne n'a pas de sélection) mais vous appelez-le de toute façon alors un 404 a du sens. Ou que vous implémentiez des réponses partielles et que / person / {id} retourne un document plus complet à moins que le client ne demande un sous-ensemble des données

Paul D'Ambra
la source
5
Vous n'avez pas expliqué pourquoi vous pensez qu'il est difficile de retourner un 404. Du point de vue du domaine, vous ne savez pas si vous avez reçu un 404 ou un 200, qui est résumé par la couche client.
Vincent Savard
14
ou peut-être vaut-il mieux renvoyer 204 sans contenu?
Paul D'Ambra
6
Je suppose que mon souci avec 404 est de lever l'ambiguïté d'un changement de configuration qui introduit un mauvais modèle d'URL d'une personne sans animal spirituel
Paul D'Ambra
2
@ PaulD'Ambra Pour ce que ça vaut, je n'utiliserais pas de no-content. Je n'ai pas vu les codes HTTP traités de cette façon en front-end en Javascript depuis les jours XmlHttpRequest. Mais c'est certainement valable.
Brandon Arnold

Réponses:

24

Les codes HTTP 4xx ne sont probablement pas le bon choix pour ce scénario. Vous déclarez que le fait d'avoir zéro animal spirituel est un état valide, et la route API person/{id}/selectedSpiritAnimaltiendra compte du fait que la personne en idpossède ou non.

Les réponses HTTP 4xx sont réservées à la situation où un client a fait quelque chose de incorrect dans la demande (voir l'archive w3 de la spécification d'origine ). Mais le client fait une demande valable, que la personne idait ou non un animal spirituel.

Je me penche donc vers les deuxièmes solutions en utilisant un corps JSON correctement formaté dans la réponse et un code HTTP 2xx.

Maintenant, si vous obtenez une telle demande et qu'il s'avère que personne idn'existe, un code 4xx a plus de sens.

Brandon Arnold
la source
17
"Les réponses HTTP 4xx sont réservées à la situation où un client a fait quelque chose de incorrect dans la demande". Lorsque vous faites des déclarations faisant autorité sur la spécification, veuillez faire référence à la spécification HTTP réelle, et non (a) à un cadre construit dessus, ou (b) à un article de blog aléatoire.
Eric Stein
5
@EricStein Je me sentais un peu paresseux à ce sujet; merci pour la critique. Mis à jour.
Brandon Arnold
1
J'ai l'impression que le lien RFC ici est un peu conflictuel. D'une part, la partie 4xx dit cases in which the client seems to have erred, mais d'autre part, la 404 dit directement The server has not found anything matching the Request-URI. Vous pourriez envisager de demander quelque chose qui n'existe pas une erreur client. Je comprends que la personne n'existe pas contre un accessoire secondaire qui n'existe pas, mais cela pourrait être indiqué comme message de réponse. 204 semble également pertinent, puisque l'entité existe, mais n'a pas de contenu pour une sous-propriété.
shortstuffsushi
1
Le client a fait quelque chose de mal; il a demandé l'animal spirituel sélectionné pour un utilisateur lorsqu'il n'existe pas. C'est exactement ce que signifie 404 ... vous avez demandé quelque chose qui n'est pas là.
Andy
5
Lorsque mon navigateur fait une demande à softwareengineering.stackexchange.com/questions/unicorn, c'est une demande valide et pourtant j'obtiens toujours un 404 (il se trouve qu'il n'y a aucun doute avec l'ID "unicorn").
user253751
24

Permettez-moi de vous présenter le modèle de maturité Richardson .

Votre problème est que vous représentez deux ressources comme une seule, où vous devriez vraiment avoir deux ressources qui ont une relation indiquée par l'hypermédia. Utiliser l'hypermédia pour décrire les relations est le glorieux niveau 3 de repos.

Votre personne doit vivre sous l'URI /person/{id}et l'animal doit vivre sous /spiritanimal/{id}. La personne doit indiquer qu'elle a un animal spirituel en utilisant un lien vers l'animal.

Imaginons une personne appelée Bob, qui a l'identifiant 123 et un animal spirituel Licorne.

GET /person/123

retournerais;

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

Maintenant, quiconque lit la personne 123 saura qu'il a un animal spirituel et a l'URI où il peut obtenir plus d'informations à ce sujet.

GET /spitiranimal/789

pourrait revenir

{
  "type": "Unicorn"
}

Imaginons maintenant une personne appelée Fred, qui a l'identifiant 456 et aucun animal spirituel.

GET /person/456

retournerais;

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

Maintenant, quiconque lit la personne 456 saura qu'il n'a pas d'animal spirituel, car il n'y a pas de lien. Il n'est pas nécessaire d'utiliser un code d'état HTTP pour représenter l'absence de relation.

Qwerky
la source
1
J'ajoutais simplement à la question que, étant donné la possibilité de changer l'API, un certain niveau de HATEOAS serait probablement la bonne solution. C'est un excellent exemple de cela
Paul D'Ambra
1

Ceci est l'URL appropriée pour obtenir des animaux spirituels; par conséquent, une erreur 404 est inappropriée. 404 est pour représenter un problème technique, pas un problème logique.

La solution appropriée consiste à renvoyer http 200 et {"selectedAnimal": null}

Vous devriez avoir une méthode Web séparée/person/{id}/hasSelectedSpiritAnimal qui revient {"isSpiritAnimalSelected": false}. Dans les coulisses, il peut ou non faire les mêmes appels de méthode, renvoyant simplement false si null, mais c'est à lui de décider, pas au code consommateur.

Il est préférable d'éviter de combiner pour séparer les requêtes en une seule méthode Web sans raison impérieuse de le faire, même si les requêtes sont étroitement liées.

TheCatWhisperer
la source
1
Pourriez-vous expliquer pourquoi vous pensez que c'est la solution appropriée? Je n'ai aucun sens de retourner des données quand il n'y a pas de données à retourner. Je suis d'accord avec vous qu'un 404 n'a pas de sens puisque l'URL est très bien, donc un 204 semble être le plus logique pour moi.
Vincent Savard
2
Les codes d'état @VincentSavard sont plus difficiles à utiliser que les réponses json et conviennent mieux aux problèmes techniques qu'à la communication des informations de domaine.
TheCatWhisperer
2
204: Aucun contenu avec un corps vide. Tout est clair et explicite. Pas besoin de discuter davantage. Dans l'ensemble, lorsqu'il n'y a pas de modèle de conception clair, un SE devrait en choisir un, le plier à son besoin et fournir une documentation. Le retour d'un corps avec une réponse 204 n'est pas conséquent.
formixian
2
@formixian ... Si vous créez une api bjson / tcp au lieu d'une json / http, que retourneriez-vous pour ce cas? S'appuyer sur les codes http me semble lier inutilement les résultats de l'api à son implémentation 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"Aucun contenu ne signifie aucun contenu . ie: il retourne "". Ces deux ne sont pas du tout similaires. "l'animal spirituel n'est pas actuellement dans le dossier" est très différent de "".
Shane
1

Ce que votre point final représente n'est pas seulement un animal; c'est un animal ou son absence. C'est une valeur mieux représentée par un Optional/ Maybe/ Nullable/ etc.

Les valeurs légitimes (comme dans 200 OK) peuvent donc être:

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

Je pourrais imaginer que la DELETEméthode, lorsqu'elle est appliquée sur le point de terminaison, peut définir 'selected'à falsenouveau, qui est, UNSET l'animal sélectionné.

Vous pouvez, bien sûr, déposer la 'selected'clé ici, elle n'est affichée que pour plus de clarté; string vs nullsuffit pour la distinction.

9000
la source
0

Vous devez utiliser 404.

Le code d'état 404 (Not Found) indique que le serveur d'origine n'a pas trouvé de représentation actuelle pour la ressource cible

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

Puisque vous créez une interface de programmation, pas une interface humaine, le texte 404 est facultatif.

Je préfère cela parce que je préfère autant que possible les protocoles standard. HTTP a un moyen de représenter la non-existence, et c'est ce que j'utiliserais.

EDIT: Supposons que vous ayez ajouté une fonctionnalité permettant aux utilisateurs de choisir de partager leurs animaux spirituels et que personne ne les partage. Souhaitez-vous retourner 200 OK nullou 200 OK "Unauthorized? Ou utiliseriez-vous le standard 401 Unauthorized / 403 Forbidden? Cela semble directement analogue à ne pas en choisir un en premier lieu.


Alternativement, si vous souhaitez utiliser 200 OK + JSON, vous devez revenir 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

Gardez les choses propres. Créez plus d'emballage seulement si nécessaire.

Paul Draper
la source
2
Les données sont cependant trouvées. Il se trouve simplement que les données sont null(sans valeur). Ceci est différent du client qui demande quelque chose pour lequel il n'existe aucun emplacement pour contenir la valeur. Vous ne suggéreriez pas de lancer un AttributeErroren Python lorsque la valeur se trouve être None; cela rendrait l'interface peu pratique et inutilement difficile à utiliser. Plus pragmatique, de nombreuses API client HTTP peuvent convertir le 404 en mécanisme de gestion des erreurs standard du langage (code d'erreur, exception, type d'erreur), ce qui signifie que vous devrez supprimer une erreur superflue.
jpmc26
@Paul Draper Faites défiler un peu dans la spécification, vous verrez qu'elle fait une déclaration générale sur HTTP 4xx: "La classe 4xx de code d'état est destinée aux cas dans lesquels le client semble avoir commis une erreur." L'op a clairement déclaré que le fait de ne pas avoir d'animal spirituel est un état valide pour lequel l'API devrait rendre compte. tools.ietf.org/html/rfc7231#section-6.5
Brandon Arnold
Comment faites-vous alors la distinction entre indiquer l'inexistence de la ressource demandée (c.-à-d. Aucun animal sélectionné) et le client demandant un chemin non valide (c. /person/{id}/selectedSpritAnimal-à- d . Observer la faute de frappe dans Sprit).
sampathsris
1
Indépendamment de l'intention, un 404 signifie strictement que la ressource n'a pas été trouvée. Si un SelectedSpiritAnimal est une ressource et qu'aucune n'existe, alors il n'y a rien à GET et un 404 est approprié. Cependant, si le client veut savoir si un animal spirituel a été sélectionné, le serveur doit exposer une autre ressource /person/{id}/isSpiritAnimalSelectedqui indiquerait au client s'il a été sélectionné. Semble pour moi le même exemple classique de tester si un fichier existe avant de le lire. Il suffit de lire le fichier et de gérer l'erreur ENOENT s'il est levé.
aaaaaa
1
@PaulDraper Le point n'est pas de savoir si la ressource existe ou non. Le point est que les codes HTTP 4xx sont destinés lorsque l'appelant utilise l'API de manière incorrecte. Op indique qu'il /person/{id}/selectedSpiritAnimalest destiné à être utilisé même si l'animal spirituel n'existe pas. Cela signifie que HTTP 4xx est incorrect, lorsqu'il est utilisé dans un tel cas. Op indique également correctement que cela peut être une odeur de mauvaise conception de l'API.
Brandon Arnold