Il existe plusieurs cas d'utilisation pour définir des 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 souhaitez renvoyer un objet à sérialiser, mais aussi un code d'état différent du 200 par défaut).
Permettez-moi donc d'essayer d'énumérer les différents cas d'utilisation et les solutions pour chacun:
1. Code d'erreur (500, 404, ...)
Cas d'utilisation le plus courant lorsque vous souhaitez renvoyer un code d'état différent de celui d' 200 OK
une erreur.
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) Lancez une exception
Dans ce cas, je pense que la façon la plus propre de gérer le problème est de lever 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 la valeur par défaut ExceptionMapper
préconfigurée avec Jersey (et je suppose que c'est la même chose avec d'autres implémentations) et jeter n'importe laquelle des sous-classes existantes de javax.ws.rs.WebApplicationException
. Ce sont des types d'exceptions 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 également définir vos propres exceptions et ExceptionMapper
classes personnalisées et ajouter ces mappeurs à Jersey au moyen de l' @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);
}
}
Fournisseur:
@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'exceptions existants que vous utilisez.
b) Utilisez le générateur Réponse
Une autre façon de définir un code d'état consiste à utiliser un Response
générateur pour créer une réponse avec le code voulu.
Dans ce cas, le type de retour de votre méthode doit être javax.ws.rs.core.Response
. Ceci est décrit dans diverses autres réponses telles que la réponse acceptée par son équipe 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. Succès, mais pas 200
Un autre cas où vous souhaitez définir le statut de retour est lorsque l'opération a réussi, mais vous souhaitez 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 lorsque vous créez une nouvelle entité ( POST
demande) et que vous souhaitez renvoyer des informations sur cette nouvelle entité ou peut-être l'entité elle-même, avec un 201 Created
code d'état.
Une approche consiste à utiliser l'objet de réponse comme décrit ci-dessus et à définir vous-même le corps de la demande. Cependant, ce faisant, vous perdez la possibilité d'utiliser la sérialisation automatique en XML ou JSON fournie par JAXB.
Il s'agit de la méthode d'origine renvoyant 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 le statut de retour sera 200, pas 201.
Maintenant, le problème est que si je veux utiliser le Response
générateur pour définir le code retour, je dois retourner un Response
objet dans ma méthode. Comment puis-je toujours renvoyer l' User
objet à sérialiser?
a) Définissez le code sur la réponse du servlet
Une approche pour résoudre ce problème consiste à obtenir un objet de demande de servlet et à définir manuellement le code de réponse, comme démontré dans 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 résurgence désagréable du code API de servlet de bas niveau 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 câble.
b) Utiliser l'objet de réponse avec l'entité
Dans ce cas, la meilleure solution consiste à utiliser l'objet Response et à définir l'entité à sérialiser sur cet objet Response. Ce serait bien de rendre l'objet Response générique pour indiquer le type de l'entité de charge utile 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 créée de la classe du générateur Réponse pour définir le code d'état sur 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 nous avions auparavant lorsque nous venions de renvoyer l'objet User. Il ajoute également un en-tête d'emplacement.
La classe Response a un certain nombre de méthodes de génération pour différents statuts (stati?) Tels que:
Response.accepted () Response.ok () Response.noContent () Response.notAcceptable ()
NB: l'objet hateoas est une classe d'aide que j'ai développée pour aider à générer des URI de ressources. Vous devrez trouver votre propre mécanisme ici;)
C'est à peu près ça.
J'espère que cette longue réponse aide quelqu'un :)