API REST 404: mauvais URI, ou ressource manquante?

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

Il semble que la pratique acceptée dans la conception d'une API REST est que si la ressource demandée n'existe pas, un 404 est renvoyé.

Cependant, pour moi, cela ajoute une ambiguïté inutile. HTTP 404 est plus traditionnellement associé à un mauvais URI. Donc, en effet, nous disons " soit vous êtes au bon endroit, mais cet enregistrement spécifique n'existe pas, ou il n'y a pas un tel emplacement sur les Internets! Je ne suis vraiment pas assurez-vous lequel..."

Considérez L'URI suivant:

http://mywebsite/api/user/13

Si je reçois un 404, est-ce parce que L'Utilisateur 13 n'existe pas? Ou est-ce parce que mon URL aurait dû {[11] } ÊTRE:

http://mywebsite/restapi/user/13

Dans le passé, je viens de renvoyer un résultat nul 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 nécessairement une pratique acceptée. Mais est-il une meilleure façon de le faire?

146
demandé sur MC Emperor 2012-03-29 21:50:57

9 réponses

404 est juste 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 vont voir.

66
répondu Robert Levy 2012-03-29 17:52:23

Utiliser 404 si la ressource n'existe pas. Ne retournez pas 200 avec un corps vide.

Ceci est similaire à undefined vs chaîne vide (par exemple, "") dans la 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à et que quelque chose est juste vide en ce moment (comme une chaîne vide dans programmation).

404 ça ne veut pas dire 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).

36
répondu nategood 2012-03-30 18:25:16

, 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 la même que la différence entre http://mywebsite/api/user/13 et http://mywebsite/api/user/14 c'est à dire pas le même n'est pas la même période. Donc, un 404 serait tout à fait approprié pour http://mywebsite/api/user/14 (si 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 vide 200 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 n'a pas de contenu ou n'est essentiellement rien. Cela signifie qu'il est telle ressource. Cependant, cela ne signifie pas nécessairement que vous prétendez qu'il y a un utilisateur persistant dans un magasin de données avec l'id 14. C'est votre préoccupation privée, Pas celle du client qui fait la demande. Donc, s'il est logique de modéliser vos ressources de cette façon, allez-y.

Il y a des implications de sécurité à donner à vos clients des informations qui leur permettraient de deviner plus facilement les URI légitimes. renvoyer un 200 sur les échecs au lieu d'un 404 peut donner au client une idée 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 malveillant le client serait capable de deviner la partie http://mywebsite/api/user de toute façon. Un meilleur remède serait d'utiliser les UUID. c'est-à-dire 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 retourner 200 sans donner beaucoup.

16
répondu jhericks 2012-03-29 22:07:18

404 Not Found signifie techniquement que l'uri ne mappe pas actuellement à une ressource. Dans votre exemple, j'interprète une requête à {[0] } qui renvoie un 404 pour impliquer que cette url a étéjamais mappée à une ressource. Pour le client, cela devrait être la fin de la conversation.

Pour répondre aux 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 à l'url http://mywebsite/api/user/13, vous souhaitez communiquer que les clients doivent utiliser l'url canonique http://mywebsite/restapi/user/13. Dans ce cas, vous pouvez envisager d'émettre une redirection permanente en retournant un 301 déplacé de façon permanente et en fournissant l'url canonique dans l'en-tête Location de la réponse. Cela indique au client que pour les demandes futures, il doit utiliser l'url canonique.

8
répondu Ralph Allan Rice 2012-08-07 16:20:51

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

404 Not Found - le service est beaucoup trop paresseux (ou sécurisé) pour nous donner une vraie raison pour laquelle notre demande a échoué, mais quelle que soit la raison, nous devons y faire face.

7
répondu Michael Dausmann 2012-11-02 22:43:18

Donc, en substance, il semble que la réponse pourrait dépendre de la façon dont la demande est formée.

Si la ressource demandée fait partie de L'URI selon une requête à http://mywebsite/restapi/user/13 et que l'utilisateur 13 n'existe pas, alors un 404 est probablement approprié et intuitif car L'URI est représentatif d'un utilisateur/entité/document / etc. inexistant. La même chose vaut 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.

Toutefois, si l'ID de ressource demandé a été inclus dans l'en-tête de la requête [incluez votre propre exemple], ou en effet, 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 D'utilisateur se termine à http://mywebsite/restapi/user/); et donc la réponse pourrait raisonnablement être un 200 (avec un message correctement verbeux) parce que l'utilisateur spécifique connu sous le nom 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 (bien que je l'ai déjà soutenu que c'est le cas). Un code de réponse 200 (sans réponse détaillée) peut empêcher l'analyse d'un problème lorsqu'un identifiant incorrect est envoyé, par exemple.

Une meilleure approche serait d'envoyer un 204 - No Contentréponse. Ceci est conforme à la description du w3c *le serveur a rempli la requête mais n'a pas besoin de renvoyer un corps d'entité et peut vouloir renvoyer des méta-informations mises à jour.*1 la confusion, à mon avis, est causée par L'entrée Wikipedia indiquant 204 Aucun contenu - le serveur a traité la demande avec succès, mais ne renvoie aucun contenu. généralement 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 facile-il suffit d'envoyer un 204 si l'entité n'existe pas. Il y a même un argument pour renvoyer un 204 au lieu d'un 404, la demande a été traitée et aucun contenu n'a été retourné! Veuillez noter que les 204 ne le font pas autoriser le contenu dans le corps de réponse

Sources

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

7
répondu john 2014-11-26 16:56:00

C'est un très vieux poste, mais j'ai fait face à un problème similaire et je voudrais partager mon expérience avec vous les gars.

Je construis une architecture microservice avec des API rest. J'ai des services de repos, ils collectent des données du système back-end en fonction des paramètres de la requête.

J'ai suivi les documents de conception de L'API rest et j'ai renvoyé HTTP 404 avec un message d'erreur JSON parfait au client lorsqu'il n'y avait pas de données alignées sur les conditions de requête (par exemple, zéro enregistrement était sélectionné).

Quand il n'y avait pas de données à 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 "non trouvé" et il a été renvoyé au client avec HTTP 404. Cela fonctionne bien.

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

Mais j'avais besoin d'écrire confusion du code supplémentaire juste parce que HTTP 404 avait deux fonctions différentes:

  • le vrai HTTP 404 lorsque l'API rest n'est pas disponible dans l'url donnée, elle est levée par le serveur d'applications ou le serveur web où l'application de L'API rest s'exécute
  • le client récupère également HTTP 404 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: mon gestionnaire D'erreur de l'API rest attrape toutes les exceptions apparaissent dans le service back-end, ce qui signifie en cas d'erreur, mon API rest retourne toujours avec un message JSON parfait avec les détails du message.

C'est la 1ère version de ma méthode client helper 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 , parce que mon client Java ou JavaScript peut recevoir deux types de HTTP 404, j'ai besoin de vérifier le corps de la réponse en cas de HTTP 404. Si je peux analyser le corps de la réponse alors je suis sûr que j'ai récupéré une réponse où il n'y avait pas de données à renvoyer pour le client.

Si Je ne suis pas capable d'analyser la réponse, cela signifie que j'ai récupéré un vrai HTTP 404 du serveur web (Pas de l'application REST API).

C'est tellement déroutant et l'application cliente doit toujours faire une analyse supplémentaire pour vérifier la vraie raison de HTTP 404.

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

Donc, au lieu D'utiliser HTTP 404 dans ces deux scénarios différents, j'ai décidé que je vais faire ce qui suit:

  • Je n'utilise plus HTTP 404 comme code HTTP de réponse dans mon application rest.
  • 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 gère mieux ce problème.

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

5
répondu zappee 2017-11-19 22:19:54

L'Identificateur de Ressource uniforme est un pointeur unique vers la ressource. Un URI de forme mal ne pointe pas vers la ressource et donc effectuer un GET dessus ne retournera pas une ressource. 404 signifie que le serveur n'a rien trouvé correspondant à L'URI de requête. Si vous mettez le mauvais URI ou le mauvais URI, c'est votre problème et la raison pour laquelle vous n'avez pas accès à une ressource, QU'il s'agisse d'une page HTML ou D'un IMG.

1
répondu suing 2012-04-06 13:10:32

Pour ce scénario, HTTP 404 est le code de réponse pour la réponse de L'API REST Comme 400, 401 , 404, 422 entité non traitable

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é lancé par L'API REST

0
répondu Venkata Naresh Babu 2018-10-04 12:19:27