Il existe plusieurs cas d'utilisation pour définir les codes d'état HTTP dans un service web REST, et au moins un n'était pas suffisamment documenté dans les réponses existantes (c'est-à-dire lorsque vous utilisez la sérialisation JSON/XML auto-magique à l'aide de JAXB, et que vous voulez renvoyer un objet à sérialiser, mais aussi un code d'état différent du 200 par défaut).
Je vais donc essayer d'énumérer les différents cas d'utilisation et les solutions pour chacun d'entre eux :
1. Code d'erreur (500, 404,...)
Le cas d'utilisation le plus courant lorsque vous souhaitez renvoyer un code d'état différent de celui de l'utilisateur. 200 OK
c'est lorsqu'une erreur se produit.
Par exemple :
- une entité est demandée mais elle n'existe pas (404)
- la demande est sémantiquement incorrecte (400)
- l'utilisateur n'est pas autorisé (401)
- il y a un problème avec la connexion à la base de données (500)
- etc.
a) Lancer une exception
Dans ce cas, je pense que la façon la plus propre de traiter le problème est de lancer une exception. Cette exception sera gérée par un ExceptionMapper
qui traduira l'exception en une réponse avec le code d'erreur approprié.
Vous pouvez utiliser l'option par défaut ExceptionMapper
qui est livré préconfiguré avec Jersey (et je suppose que c'est la même chose avec d'autres implémentations) et lancer n'importe quelle sous-classe existante de javax.ws.rs.WebApplicationException
. Il s'agit de types d'exception prédéfinis qui sont mappés à différents codes d'erreur, par exemple :
- BadRequestException (400)
- InternalServerErrorException (500)
- NotFoundException (404)
Etc. Vous pouvez trouver la liste ici : API
Vous pouvez aussi définir vos propres exceptions et ExceptionMapper
et d'ajouter ces mappeurs à Jersey par le biais des @Provider
annotation ( source de cet exemple ):
public class MyApplicationException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public MyApplicationException() {
super();
}
public MyApplicationException(String msg) {
super(msg);
}
public MyApplicationException(String msg, Exception e) {
super(msg, e);
}
}
Prestataire :
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Remarque : vous pouvez également écrire des ExceptionMappers pour les types d'exception existants que vous utilisez.
b) Utiliser le constructeur de réponses
Une autre façon de définir un code d'état est d'utiliser une fonction Response
pour construire une réponse avec le code prévu.
Dans ce cas, le type de retour de votre méthode doit être javax.ws.rs.core.Response
. Ce processus est décrit dans diverses autres réponses, comme la réponse acceptée par hisdrewness, et ressemble à ceci :
@GET
@Path("myresource({id}")
public Response retrieveSomething(@PathParam("id") String id) {
...
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Resource not found for ID: " + uuid).build();
}
...
}
2. Un succès, mais pas 200
Un autre cas où vous voulez définir l'état de retour est celui où l'opération a réussi, mais où vous voulez renvoyer un code de réussite différent de 200, ainsi que le contenu que vous renvoyez dans le corps.
Un cas d'utilisation fréquent est celui de la création d'une nouvelle entité ( POST
) et veulent renvoyer des informations sur cette nouvelle entité ou peut-être l'entité elle-même, ainsi qu'une demande d'accès à l'information. 201 Created
code de statut.
Une approche consiste à utiliser l'objet réponse comme décrit ci-dessus et à définir vous-même le corps de la requête. Cependant, en procédant ainsi, vous perdez la possibilité d'utiliser la sérialisation automatique en XML ou JSON fournie par JAXB.
C'est la méthode originale qui renvoie un objet entité qui sera sérialisé en JSON par JAXB :
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
Cela renverra une représentation JSON de l'utilisateur nouvellement créé, mais l'état de retour sera 200, et non 201.
Maintenant, le problème est que si je veux utiliser l'option Response
pour définir le code de retour, je dois renvoyer un fichier Response
dans ma méthode. Comment puis-je toujours retourner l'objet User
à sérialiser ?
a) Définir le code sur la réponse de la servlet
Une approche pour résoudre ce problème consiste à obtenir un objet de requête de servlet et à définir manuellement le code de réponse, comme le montre la réponse de Garett Wilson :
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, @Context final HttpServletResponse response){
User newUser = ...
//set HTTP code to "201 Created"
response.setStatus(HttpServletResponse.SC_CREATED);
try {
response.flushBuffer();
}catch(Exception e){}
return newUser;
}
La méthode renvoie toujours un objet entité et le code d'état sera 201.
Notez que pour que cela fonctionne, j'ai dû vider la réponse. Il s'agit d'une désagréable résurgence de code de bas niveau de l'API Servlet dans notre belle ressource JAX_RS, et pire encore, cela rend les en-têtes non modifiables après cela, car ils ont déjà été envoyés sur le fil.
b) Utiliser l'objet réponse avec l'entité
La meilleure solution, dans ce cas, est d'utiliser l'objet Réponse et de définir l'entité à sérialiser sur cet objet Réponse. Il serait bien de rendre l'objet Response générique pour indiquer le type de l'entité payload dans ce cas, mais ce n'est pas le cas actuellement.
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response addUser(User user){
User newUser = ...
return Response.created(hateoas.buildLinkUri(newUser, "entity")).entity(restResponse).build();
}
Dans ce cas, nous utilisons la méthode created de la classe Response builder afin de définir le code d'état 201. Nous passons l'objet entité (utilisateur) à la réponse via la méthode entity().
Le résultat est que le code HTTP est 401 comme nous le voulions, et le corps de la réponse est exactement le même JSON que celui que nous avions auparavant lorsque nous avons simplement renvoyé l'objet User. Il ajoute également un en-tête d'emplacement.
La classe Response possède un certain nombre de méthodes de construction pour différents statuts (stati ?) tels que :
Réponse.acceptée() Response.ok() Réponse.noContent() Réponse.notAcceptable()
NB : l'objet hateoas est une classe d'aide que j'ai développée pour aider à générer les URI des ressources. Vous devrez trouver votre propre mécanisme ici ;)
C'est à peu près tout.
J'espère que cette longue réponse aidera quelqu'un :)
2 votes
Quelle est la différence entre les 500 que vous voulez (irréels ? :) ) et les 500 réels ?
0 votes
@razor Ici, le vrai 500 signifie une page d'erreur HTML au lieu d'une réponse JSON.
0 votes
Le navigateur web n'a pas été conçu pour travailler avec JSON, mais avec des pages HTML, donc si vous répondez avec 500 (et même un corps de message), le navigateur peut vous montrer juste un message d'erreur (cela dépend vraiment de l'implémentation du navigateur), juste parce que c'est utile pour un utilisateur normal.