jax java jax-rs

java - Se requiere @QueryParam en JAX-RS(y qué hacer en su ausencia)



jax-rs vs jax-ws (4)

Implemento un componente de servicios web en JBoss Application Server 7 utilizando la implementación RESTEasy JAX-RS .

¿Hay una anotación disponible para declarar los parámetros obligatorios y obligatorios de @QueryParam en JAX-RS ? Y, de no ser así, ¿cuál es la manera "estándar" de lidiar con situaciones en las que faltan tales parámetros?

Los métodos de mi servicio web (recurso) devuelven resultados JSON-stringified cuando se invocan correctamente con todos los argumentos obligatorios, pero no estoy seguro de cuál es la mejor manera de indicar a la persona que llama que falta un parámetro requerido.


Buena pregunta. Desafortunadamente (o quizás afortunadamente) no hay ningún mecanismo en JAX-RS para hacer que los parámetros sean obligatorios. Si no se proporciona un parámetro, su valor será NULL y su recurso debe tratarlo en consecuencia. Recomendaría utilizar WebApplicationException para informar a sus usuarios:

@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 }


Me encontré con el mismo problema y decidí que no quería un millón de cheques nulos repartidos en mi código REST, así que esto es lo que decidí hacer:

  1. Cree una anotación que haga que se genere una excepción cuando no se especifica un parámetro requerido.
  2. Maneje la excepción lanzada de la misma manera que yo manejo todas las demás excepciones lanzadas en mi código REST.

Para 1) , implementé la siguiente anotación:

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. }

... y el siguiente ContainerRequestFilter JAX-RS ContainerRequestFilter para ejecutarlo:

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()); } } } } }

@Provider registrar el ContainerRequestFilter de la misma manera que registraría sus otras clases de @Provider en su biblioteca JAX-RS. Tal vez RESTEasy lo haga por ti automáticamente.

Para 2) , manejo todas las excepciones de tiempo de ejecución utilizando un ExceptionMapper JAX-RS genérico:

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(); } }

Como antes, recuerde registrar la clase con su biblioteca JAX-RS.


Probablemente la forma más fácil es usar @Nonnull desde javax.annotation para lograr esto. Es super simple de usar, ya que todo lo que tiene que hacer es agregarlo antes de @QueryParam como se muestra a continuación.

Sin embargo, tenga en cuenta que esto generará una IllegalArgumentException cuando el parámetro sea nulo, por lo que la respuesta que envíe será lo que haga para obtener una excepción. Si no lo interceptas, será un 500 Server Error , aunque lo correcto para devolverlo será una 400 Bad Request . Puede interceptar la IllegalArgumentException y procesarla para devolver una respuesta adecuada.

Ejemplo:

import javax.annotation.Nonnull; ... @GET @Path("/your-path") public Response get(@Nonnull @QueryParam("paramName") String paramName) { ... }

El mensaje de error predeterminado devuelto a la persona que llama se ve así:

{"timestamp": 1536152114437, "estado": 500, "error": "Error interno del servidor", "excepción": "java.lang.IllegalArgumentException", "mensaje": "Argumento para el parámetro ''ParamName'' de @Nonnull parámetro de com /example/YourClass.get no debe ser nulo "," path ":" / path / to / your-path "}


Puede usar javax.validation anotaciones javax.validation para imponer que los parámetros son obligatorios @javax.validation.constraints.NotNull con @javax.validation.constraints.NotNull . Vea un ejemplo para Jersey y otro para RESTeasy .

Entonces tu método simplemente se convertiría en:

@GET @Path("/some-path") public String read(@NotNull @QueryParam("name") String name) { String something = // implementation return something; }

Tenga en cuenta que la excepción se traduce luego por el proveedor de JAX-RS a algún código de error. Por lo general, se puede anular al registrar su propia implementación de javax.ws.rs.ext.ExceptionMapper<javax.validation.ValidationException> .

Esto proporciona una forma centralizada de traducir el parámetro obligatorio a respuestas de error y no es necesaria la duplicación de código.