Je déploie un composant de services Web sur JBoss Application Server 7 à l'aide de l'implémentation RESTEasy JAX-RS.
Existe-t-il une annotation disponible à déclarer obligatoire, obligatoire @ QueryParam paramètres in JAX-RS? Et, si non, quelle est la manière "standard" de traiter les situations dans lesquelles de tels paramètres manquent?
Mes méthodes de service Web (ressources) renvoient des résultats sous forme de chaîne JSON lorsqu'elles sont correctement appelées avec tous les arguments obligatoires, mais je ne sais pas quel est le meilleur moyen d'indiquer à l'appelant qu'un paramètre requis était manquant.
Bonne question. Malheureusement (ou peut-être heureusement), JAX-RS ne dispose d'aucun mécanisme pour rendre les paramètres obligatoires. Si un paramètre n'est pas fourni, sa valeur sera NULL
et votre ressource devrait le gérer en conséquence. Je recommanderais d'utiliser WebApplicationException
pour informer vos utilisateurs:
@GET
@Path("/some-path")
public String read(@QueryParam("name") String name) {
if (name == null) {
throw new WebApplicationException(
Response.status(HttpURLConnection.HTTP_BAD_REQUEST)
.entity("name parameter is mandatory")
.build()
);
}
// continue with a normal flow
}
Vous pouvez utiliser javax.validation
annotations pour imposer que les paramètres soient obligatoires en les annotant avec @javax.validation.constraints.NotNull
. Voir n exemple pour Jersey et n pour RESTeasy .
Donc, votre méthode deviendrait simplement:
@GET
@Path("/some-path")
public String read(@NotNull @QueryParam("name") String name) {
String something =
// implementation
return something;
}
Notez que l'exception est ensuite traduite par le fournisseur JAX-RS en un code d'erreur. Vous pouvez généralement le remplacer en enregistrant votre propre implémentation de javax.ws.rs.ext.ExceptionMapper<javax.validation.ValidationException>
.
Cela fournit un moyen centralisé de traduire le paramètre obligatoire en réponses d'erreur et aucune duplication de code n'est nécessaire.
J'ai rencontré le même problème et décidé que je ne voulais pas d'un gazillion boilerplate null checks dispersés sur mon code REST), c'est donc ce que j'ai décidé de faire:
Pour 1), j'ai mis en œuvre l'annotation suivante:
import Java.lang.annotation.Documented;
import Java.lang.annotation.ElementType;
import Java.lang.annotation.Retention;
import Java.lang.annotation.RetentionPolicy;
import Java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Required
{
// This is just a marker annotation, so nothing in here.
}
... et le JAX-RS suivant ContainerRequestFilter
pour l'appliquer:
import Java.lang.reflect.Parameter;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
@Provider
public class RequiredParameterFilter implements ContainerRequestFilter
{
@Context
private ResourceInfo resourceInfo;
@Override
public void filter(ContainerRequestContext requestContext)
{
// Loop through each parameter
for (Parameter parameter : resourceInfo.getResourceMethod().getParameters())
{
// Check is this parameter is a query parameter
QueryParam queryAnnotation = parameter.getAnnotation(QueryParam.class);
// ... and whether it is a required one
if (queryAnnotation != null && parameter.isAnnotationPresent(Required.class))
{
// ... and whether it was not specified
if (!requestContext.getUriInfo().getQueryParameters().containsKey(queryAnnotation.value()))
{
// We pass the query variable name to the constructor so that the exception can generate a meaningful error message
throw new YourCustomRuntimeException(queryAnnotation.value());
}
}
}
}
}
Vous devez enregistrer le ContainerRequestFilter
de la même manière que vous enregistrez votre autre @Provider
cours avec votre bibliothèque JAX-RS. Peut-être que RESTEasy le fait pour vous automatiquement.
Pour 2), je gère toutes les exceptions d'exécution à l'aide d'un JAX-RS générique ExceptionMapper
:
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
public class MyExceptionMapper implements ExceptionMapper<RuntimeException>
{
@Override
public Response toResponse(RuntimeException ex)
{
// In this example, we just return the .toString() of the exception.
// You might want to wrap this in a JSON structure if this is a JSON API, for example.
return Response
.status(Response.Status.BAD_REQUEST)
.entity(ex.toString())
.build();
}
}
Comme auparavant, n'oubliez pas d'enregistrer le cours auprès de votre bibliothèque JAX-RS.
Le moyen le plus simple consiste probablement à utiliser @Nonnull
À partir de javax.annotation
Pour y parvenir. C'est super simple à utiliser car tout ce que vous avez à faire est de l'ajouter avant @QueryParam
Comme indiqué ci-dessous.
Cependant, gardez à l'esprit que cela jettera un IllegalArgumentException
lorsque le paramètre est null, ainsi la réponse que vous renverrez sera celle que vous fassiez pour une exception. Si vous ne l'interceptez pas, il s'agira d'un 500 Server Error
Même si la bonne chose à renvoyer serait un 400 Bad Request
. Vous pouvez intercepter IllegalArgumentException
et le traiter pour renvoyer une réponse appropriée.
Exemple:
import javax.annotation.Nonnull;
...
@GET
@Path("/your-path")
public Response get(@Nonnull @QueryParam("paramName") String paramName) {
...
}
Le message d'erreur par défaut renvoyé à l'appelant ressemble à ceci:
{"timestamp": 1536152114437, "status": 500, "erreur": "Erreur interne du serveur", "exception": "Java.lang.IllegalArgumentException", "message": "Argument pour le paramètre @Nonnull 'paramName' de com /example/YourClass.get ne doit pas être null "," chemin ":"/chemin/vers/votre-chemin "}