Disons que j'ai un simple Jersey REST ressource comme suit:
@Path("/foos")
public class MyRestlet
extends BaseRestlet
{
@GET
@Path("/{fooId}")
@Produces(MediaType.APPLICATION_XML)
public Response getFoo(@PathParam("fooId") final String fooId)
throws IOException, ParseException
{
final Foo foo = fooService.getFoo(fooId);
if (foo != null)
{
return Response.status(Response.Status.OK).entity(foo).build();
}
else
{
return Response.status(Response.Status.NOT_FOUND).build();
}
}
}
D'après le code ci-dessus, est-il correct de renvoyer un NOT_FOUND
statut (404
), ou devrais-je retourner 204
, ou un autre code plus approprié?
Une réponse 404 dans ce cas est assez typique et facile à consommer par les utilisateurs d'API.
L'un des problèmes est qu'il est difficile pour un client de savoir s'il a obtenu un numéro 404 en raison de la non-découverte de l'entité concernée ou d'un problème structurel dans l'URI. Dans votre exemple, /foos/5
pourrait retourner 404 car le foo avec id = 5 n'existe pas. Toutefois, /food/1
retournera 404 même si foo avec id=1
existe (parce que foos
est mal orthographié). En d'autres termes, 404 signifie soit un URI mal construit, soit une référence à une ressource inexistante.
Un autre problème survient lorsque vous avez un URI faisant référence à plusieurs ressources. Avec une simple réponse 404, le client n'a aucune idée des ressources non référencées.
Ces deux problèmes peuvent être partiellement atténués en renvoyant des informations supplémentaires dans le corps de la réponse afin de permettre à l'appelant de savoir exactement ce qui n'a pas été trouvé.
Oui, il est assez courant de renvoyer 404 pour une ressource introuvable. Tout comme une page Web, quand elle n'est pas trouvée, vous obtenez un 404. Ce n'est pas seulement REST, mais une norme HTTP.
Chaque ressource devrait avoir un emplacement d'URL. Les URL ne doivent pas nécessairement être statiques, elles peuvent être basées sur un modèle . Il est donc possible que l'URL demandée réelle n'ait pas de ressource. Il incombe au serveur de séparer l'URL du modèle pour rechercher la ressource. Si la ressource n'existe pas, c'est "Introuvable"
Voici le HTTP 1.1 spec
404 introuvable
Le serveur n'a rien trouvé qui corresponde à l'URI de demande. Aucune indication n'est donnée quant à savoir si la condition est temporaire ou permanente. Le code d'état 410 (Gone) DEVRAIT être utilisé si le serveur sait, par le biais d'un mécanisme configurable en interne, qu'une ancienne ressource est indisponible en permanence et ne possède pas d'adresse de transfert. Ce code d'état est couramment utilisé lorsque le serveur ne souhaite pas révéler exactement pourquoi la demande a été refusée ou lorsqu'aucune autre réponse n'est applicable.
Voici pour 204
204 pas de contenu
Le serveur a répondu à la demande mais n'a pas besoin de renvoyer un corps d'entité et peut vouloir renvoyer des métainformations mises à jour. La réponse PEUT inclure des métainformations nouvelles ou mises à jour sous la forme d'en-têtes d'entité, qui, le cas échéant, DEVRAIENT être associées à la variante demandée.
Si le client est un agent d'utilisateur, il NE DEVRAIT PAS changer sa vue de document par rapport à celle qui a provoqué l'envoi de la demande. Cette réponse est principalement destinée à permettre la saisie d'actions sans provoquer de modification de la vue de document active de l'agent d'utilisateur, bien que toute métainformation nouvelle ou mise à jour DEVRAIT être appliquée au document actuellement dans la vue active de l'agent de l'utilisateur.
La réponse 204 NE DOIT PAS inclure de corps de message et est donc toujours terminée par la première ligne vide après les champs d'en-tête.
Normalement, 204 serait utilisé lorsqu'une représentation a été mise à jour ou créée et qu'il n'est pas nécessaire de renvoyer un corps de réponse. Dans le cas d'un POST, vous pouvez ne renvoyer que l'emplacement de la ressource nouvellement créée. Quelque chose comme
@POST
@Path("/something")
@Consumes(...)
public Response createBuzz(Domain domain, @Context UriInfo uriInfo) {
int domainId = // create domain and get created id
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
builder.path(Integer.toString(domainId)); // concatenate the id.
return Response.created(builder.build()).build();
}
La created(URI)
renverra la réponse avec l'URI nouvellement créé dans l'en-tête Location
.
Ajout à la première partie. N'oubliez pas que chaque demande émanant d'un client est une demande d'accès à une ressource, que ce soit simplement pour l'obtenir ou la mettre à jour avec PUT. Et une ressource peut être n'importe quoi sur le serveur. Si la ressource n'existe pas, une réponse générale serait de dire au client que nous ne pouvons pas trouver cette ressource.
Pour développer votre exemple. Disons que FooService
accède à la base de données. Chaque ligne de la base de données peut être considérée comme une ressource. Et chacune de ces lignes (ressources) a une URL unique, comme foo/db/1
Pourrait localiser une ligne avec une clé primaire 1. Si l'identifiant est introuvable, alors ressource = est "Introuvable"
Un code d'erreur 4XX
Signifie une erreur du côté client.
Lorsque vous demandez une ressource statique sous forme d'image ou de page html, le renvoi d'une réponse 404
est logique:
Le code de réponse d'erreur client HTTP 404 non trouvé indique que le serveur ne peut pas trouver la ressource demandée. Les liens menant à une page 404 sont souvent appelés liens brisés ou morts et peuvent être sujets à la pourriture.
Lorsque vous fournissez aux clients certaines méthodes REST, vous utilisez les méthodes HTTP, mais vous ne devez pas considérer les services REST comme de simples ressources.
Pour les clients, une réponse d'erreur dans la méthode REST est souvent traitée à proximité des erreurs d'autres traitements.
Par exemple, pour intercepter les erreurs lors des invocations REST ou ailleurs), les clients peuvent utiliser catchError()
de RxJS .
Nous pourrions écrire un code (dans TypeScript/Angular 2 pour le code exemple) de cette manière pour déléguer le traitement des erreurs à une fonction:
return this.http
.get<Foo>("/api/foos")
.pipe(
catchError(this.handleError)
)
.map(foo => {...})
Le problème est que toute erreur HTTP (5XX ou 4XXX) se terminera par le rappel catchError()
.
Cela peut réellement rendre les réponses de l’API REST) trompeuses pour les clients.
Si nous faisons un parallèle avec le langage de programmation, nous pourrions considérer 5XX/4XX comme un flux d’exceptions.
Généralement, nous ne lançons pas d’exception uniquement parce qu’une donnée n’est pas trouvée , nous le lançons car une donnée n’est pas trouvée et que ces données auraient été trouvées .
Pour l'API REST, nous devrions suivre la même logique.
Si l'entité peut ne peut être trouvée, retourner OK
dans les deux cas est parfaitement correct:
@GET
@Path("/{fooId}")
@Produces(MediaType.APPLICATION_XML)
public Response getFoo(@PathParam("fooId") final String fooId)
throws IOException, ParseException {
final Foo foo = fooService.getFoo(fooId);
if (foo != null){
return Response.status(Response.Status.OK).entity(foo).build();
}
return Response.status(Response.Status.OK).build();
}
Le client pourrait ainsi gérer le résultat en fonction du résultat, qu'il soit présent ou manquant.
Je ne pense pas que renvoyer 204
Apporte une valeur utile.
La documentation HTTP 204
indique que:
Le client n'a pas besoin de quitter sa page actuelle.
Mais demander une ressource REST et plus particulièrement par une méthode GET ne signifie pas que le client est sur le point de mettre fin à un flux de travail (ce qui est plus logique avec les méthodes POST/PUT).
Le document ajoute aussi:
Le cas d'utilisation courant consiste à renvoyer 204 à la suite d'une demande PUT, en mettant à jour une ressource, sans modifier le contenu actuel de la page affichée à l'utilisateur.
Nous ne sommes vraiment pas dans ce cas.
Certains codes HTTP spécifiques à la navigation classique se marient très bien avec les codes de retour de REST API (201, 202, 401, et ainsi de suite) ...), mais ce n'est pas toujours le cas. , plutôt que de tordre les codes originaux, je préférerais les garder simples en utilisant des codes plus généraux: 200
, 400
.