web-dev-qa-db-fra.com

REST API 404: URI incorrect ou ressource manquante?

Je construis une API REST, mais j'ai rencontré un problème.

Il semble que la pratique acceptée pour la conception d'une API REST consiste à renvoyer une 404 si la ressource demandée n'existe pas.

Cependant, pour moi, cela ajoute une ambiguïté inutile. HTTP 404 est plus traditionnellement associé à un mauvais URI. Donc, en réalité, nous disons "Soit vous êtes au bon endroit, mais cet enregistrement spécifique n'existe pas, o il n'y a pas un tel emplacement sur Internet! I ne suis vraiment pas sûr lequel ... "

Considérons l'URI suivant:

http://mywebsite/api/user/13

Si je récupère une 404, est-ce parce que l'utilisateur 13 n'existe pas? Ou est-ce parce que mon URL devrait avoir été:

http://mywebsite/restapi/user/13

Dans le passé, je viens de renvoyer un résultat NULL avec un code de réponse HTTP 200 OK si l'enregistrement n'existe pas. C'est simple, et à mon avis très propre, même si ce n'est pas forcément une pratique acceptée. Mais y a-t-il une meilleure façon de faire cela?

182
Brian Lacy

404 n'est que le code de réponse HTTP. En plus de cela, vous pouvez fournir un corps de réponse et/ou d'autres en-têtes avec un message d'erreur plus significatif que les développeurs verront.

82
Robert Levy

tilisez 404 si la ressource n'existe pas. Ne renvoyez pas 200 avec un corps vide.

Cela s'apparente à undefined vs chaîne vide (par exemple "") en programmation. Bien que très similaire, il y a certainement une différence.

404 signifie que rien n'existe à cet URI (comme une variable indéfinie en programmation). Retourner 200 avec un corps vide signifie que quelque chose existe là-bas et que quelque chose est juste vide pour le moment (comme une chaîne vide dans la programmation).

404 ne signifie pas que c'était un "mauvais URI". Il existe des codes HTTP spéciaux destinés aux erreurs URI (par exemple 414 Request-URI Too Long).

47
nategood

Comme avec la plupart des choses, "ça dépend". Mais pour moi, votre pratique n’est pas mauvaise et ne va pas à l’encontre de la spécification HTTP en soi. Cependant, clarifions certaines choses.

Tout d'abord, les URI doivent être opaques. Même s'ils ne sont pas opaques pour les gens, ils sont opaques pour les machines. En d’autres termes, la différence entre http://mywebsite/api/user/13, http://mywebsite/restapi/user/13 est identique à la différence entre http://mywebsite/api/user/13 et http://mywebsite/api/user/14 c’est-à-dire qu’elle n’est pas la même période. Donc, un 404 serait tout à fait approprié pour http://mywebsite/api/user/14 (s'il n'y a pas un tel utilisateur), mais pas nécessairement la seule réponse appropriée.

Vous pouvez également renvoyer une réponse 200 vide ou plus explicitement une réponse 204 (pas de contenu). Cela transmettrait autre chose au client. Cela impliquerait que la ressource identifiée par http://mywebsite/api/user/14 soit dépourvue de contenu ou ne soit essentiellement rien. Cela signifie qu'il y a est une telle ressource. Toutefois, cela ne signifie pas nécessairement que vous déclarez qu'un utilisateur est persistant dans un magasin de données portant l'ID 14. C'est votre préoccupation privée et non celle du client qui fait la demande. Donc, s'il est logique de modéliser vos ressources de cette façon, allez-y.

Donner à vos clients des informations qui faciliteraient la tâche pour deviner les adresses URI légitimes a certaines implications en termes de sécurité. Le fait de renvoyer 200 messages manqués au lieu de 404 peut indiquer au client qu'au moins la partie http://mywebsite/api/user est correcte. Un client malveillant pourrait simplement continuer à essayer différents entiers. Mais pour moi, un client malveillant serait en mesure de deviner la partie http://mywebsite/api/user de toute façon. Un meilleur remède serait d'utiliser les UUID. c'est-à-dire que http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 est meilleur que http://mywebsite/api/user/14. En faisant cela, vous pourriez utiliser votre technique de restitution de 200 sans trop en révéler.

17
jhericks

404 Introuvable signifie techniquement qu'uri ne mappe pas actuellement une ressource. Dans votre exemple, j'interprète une demande à http://mywebsite/api/user/13 qui renvoie un 404, ce qui implique que cette adresse URL n'a pas été jamais mappé sur une ressource. Pour le client, cela devrait être la fin de la conversation.

Pour résoudre les problèmes d'ambiguïté, vous pouvez améliorer votre API en fournissant d'autres codes de réponse. Par exemple, supposons que vous souhaitiez autoriser les clients à émettre des requêtes GET sur l'URL http://mywebsite/api/user/13, vous souhaitez indiquer que les clients doivent utiliser l'URL canonique http://mywebsite/restapi/user/13. Dans ce cas, vous pouvez envisager de créer une redirection permanente en renvoyant un 301 déplacé en permanence et en fournissant l'URL canonique dans Emplacement en-tête de la réponse. Cela indique au client que pour les futures demandes, il doit utiliser l'URL canonique.

11
Ralph Allan Rice

Donc, en substance, il semble que la réponse pourrait dépendre de la manière dont la demande est formée.

Si la ressource demandée fait partie de l'URI conformément à une demande de http://mywebsite/restapi/user/13 et que l'utilisateur 13 n'existe pas, un 404 est probablement approprié et intuitif car l'URI est représentatif d'un utilisateur/entité/document/existant inexistant. etc. Il en va de même pour la technique plus sécurisée utilisant un GUID http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 et l'argument api/restapi ci-dessus.

Cependant, si l’ID de ressource demandé était inclus dans l’en-tête de la demande [incluez votre propre exemple], ou même dans l’URI en tant que paramètre, par exemple http://mywebsite/restapi/user/?UID=13, alors l’URI serait toujours correct (car le concept USER ne se termine à http://mywebsite/restapi/user/); et par conséquent, on pourrait raisonnablement s'attendre à ce que la réponse soit 200 (avec un message verbalement approprié) car l'utilisateur spécifique appelé 13 n'existe pas, mais l'URI existe. De cette façon, nous disons que l'URI est bon, mais la demande de données n'a pas de contenu.

Personnellement, un 200 ne se sent toujours pas bien (même si je l'ai déjà expliqué). Un code de réponse 200 (sans réponse prolixe) peut entraîner la non-recherche d'un problème lorsqu'un ID incorrect est envoyé, par exemple.

Une meilleure approche consisterait à envoyer une réponse 204 - No Content. Ceci est conforme à la description de w3c * Le serveur a répondu à la demande, mais n'a pas besoin de renvoyer un corps d'entité et peut vouloir renvoyer les métainformations mises à jour. * 1 La confusion est, à mon avis, causée par l'entrée de Wikipédia indiquant 204 Pas de contenu - Le serveur a traité la demande mais n'a renvoyé aucun contenu. Habituellement utilisé comme réponse à une demande de suppression réussie . La dernière phrase est très discutable. Considérez la situation sans cette phrase et la solution est simple - envoyez simplement un 204 si l'entité n'existe pas. Il y a même un argument pour retourner un 204 au lieu d'un 404, la demande a été traitée et aucun contenu n'a été renvoyé! Veuillez noter cependant que 204 n'autorise pas le contenu dans le corps de la réponse

Sources

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 1. http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

8
john

Cet article ancien mais excellent ... http://www.infoq.com/articles/webber-rest-workflow le dit à ce sujet ...

404 Not Found - Le service est beaucoup trop paresseux (ou sécurisé) pour nous donner une raison réelle de l'échec de notre demande, mais quelle que soit la raison, nous devons en tenir compte.

7
Michael Dausmann

C’est un très vieux billet, mais j’ai fait face à un problème similaire et j’aimerais partager mon expérience avec vous.

Je construis une architecture microservice avec des API de repos. J'ai quelques services GET de repos, ils collectent des données à partir du système back-end en fonction des paramètres de la demande.

J'ai suivi les documents de conception d'API restants et j'ai renvoyé HTTP 404 avec un message d'erreur JSON parfait au client lorsqu'il n'y avait pas de données qui s'alignaient sur les conditions de la requête (par exemple, aucun enregistrement n'a été sélectionné).

Lorsqu'il n'y avait aucune donnée à renvoyer au client, j'ai préparé un message JSON parfait avec un code d'erreur interne, etc. pour informer le client de la raison du message "Introuvable". Ce message a été renvoyé au client avec HTTP 404. fonctionne bien.

Plus tard, j'ai créé une classe cliente API de repos qui est une aide facile pour masquer le code associé à la communication HTTP et que j'ai utilisé tout le temps lorsque j'ai appelé mes API de repos à partir de mon code.

MAIS j'avais besoin d'écrire du code supplémentaire déroutant simplement parce que HTTP 404 avait deux fonctions différentes:

  • le vrai HTTP 404 lorsque l'API restante n'est pas disponible dans l'URL donnée, elle est renvoyée par le serveur d'applications ou le serveur Web sur lequel l'application API restante est exécutée.
  • le client récupère HTTP 404 également lorsqu'il n'y a pas de données dans la base de données en fonction de la condition where de la requête.

Important: Le gestionnaire d'erreurs de mon API restante détecte que toutes les exceptions apparaissent dans le service back-end, ce qui signifie qu'en cas d'erreur, mon API restante renvoie toujours un message JSON parfait avec les détails du message.

Il s'agit de la 1ère version de la méthode d'assistance client qui gère les deux réponses HTTP 404 différentes:

public static String getSomething(final String uuid) {
    String serviceUrl = getServiceUrl();
    String path = "user/" + , uuid);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_UTF8)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        // HTTP 200
        return response.readEntity(String.class);
    } else {
        // confusing code comes here just because
        // I need to decide the type of HTTP 404...

        // trying to parse response body
        try {
            String responseBody = response.readEntity(String.class);
            ObjectMapper mapper = new ObjectMapper();
            ErrorInfo errorInfo = mapper.readValue(responseBody, ErrorInfo.class);

            // re-throw the original exception
            throw new MyException(errorInfo);

        } catch (IOException e) {
            // this is a real HTTP 404
            throw new ServiceUnavailableError(response, requestUrl, httpMethod);
        }

    // this exception will never be thrown
    throw new Exception("UNEXPECTED ERRORS, BETTER IF YOU DO NOT SEE IT IN THE LOG");
}

MAIS, car mon Java ou client JavaScript peut recevoir deux types de HTTP 404 en quelque sorte. Je dois vérifier le corps de la réponse dans le cas de HTTP 404. Si je peux analyser le corps de la réponse, alors je suis sûr d'avoir reçu une réponse dans laquelle il n'y avait aucune donnée à renvoyer au client.

Si je ne parviens pas à analyser la réponse, cela signifie que je récupère un véritable HTTP 404 du serveur Web (et non de l'application API restante).

C'est tellement déroutant que l'application cliente doit toujours effectuer des analyses supplémentaires pour vérifier la véritable raison de HTTP 404.

Honnêtement, je n'aime pas cette solution. C'est déroutant, il faut ajouter du code de connerie supplémentaire aux clients tout le temps.

Au lieu d'utiliser HTTP 404 dans ces deux scénarios différents, j'ai décidé de procéder comme suit:

  • Je n'utilise plus HTTP 404 comme code de réponse HTTP dans mon application de repos.
  • Je vais utiliser HTTP 204 (pas de contenu) au lieu de HTTP 404.

Dans ce cas, le code client peut être plus élégant:

public static String getString(final String processId, final String key) {
    String serviceUrl = getServiceUrl();
    String path = String.format("key/%s", key);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    log(requestUrl);

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_JSON_UTF8)
            .header(CustomHttpHeader.PROCESS_ID, processId)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        return response.readEntity(String.class);
    } else {
        String body = response.readEntity(String.class);
        ObjectMapper mapper = new ObjectMapper();
        ErrorInfo errorInfo = mapper.readValue(body, ErrorInfo.class);
        throw new MyException(errorInfo);
    }

    throw new AnyServerError(response, requestUrl, httpMethod);
}

Je pense que cela résout mieux le problème.

Si vous avez une meilleure solution, partagez-la avec nous.

6
zappee

L'identificateur de ressource uniforme est un pointeur unique sur la ressource. Un URI de forme médiocre ne pointe pas vers la ressource et par conséquent, effectuer une opération GET dessus ne retournera pas de ressource. 404 signifie que le serveur n'a rien trouvé qui corresponde à l'URI de demande. Si vous avez entré le mauvais URI ou le mauvais URI, c'est votre problème et la raison pour laquelle vous n'êtes pas parvenu à une ressource, qu'il s'agisse d'une page HTML ou d'un IMG.

1
suing

Pour ce scénario, HTTP 404 est le code de réponse pour la réponse de la REST API, telle que 400, 401, 404, 422 entités non traitées.

utilisez la gestion des exceptions pour vérifier le message d’exception complet.

try{
  // call the rest api
} catch(RestClientException e) {
     //process exception
     if(e instanceof HttpStatusCodeException){
        String responseText=((HttpStatusCodeException)e).getResponseBodyAsString();
         //now you have the response, construct json from it, and extract the errors
         System.out.println("Exception :" +responseText);
     }

}

Ce bloc d'exception vous donne le message approprié envoyé par l'API REST

0