Pregunta

Estoy aprendiendo JAX-RS (aka, JSR-311) utilizando Jersey. exitosamente He creado un recurso de raíz y estoy jugando con los parámetros:

@Path("/hello")
public class HelloWorldResource {

    @GET
    @Produces("text/html")
    public String get(
        @QueryParam("name") String name,
        @QueryParam("birthDate") Date birthDate) {

         // Return a greeting with the name and age
    }
}

Esto funciona muy bien, y se ocupa de cualquier formato en la localidad actual que es entendido por el constructor Date (String) (como AAAA / mes / día y mes / día / año). Pero si yo proporciono un valor que no es válido o no se entiende, consigo una respuesta 404.

Por ejemplo:

GET /hello?name=Mark&birthDate=X

404 Not Found

¿Cómo puedo personalizar este comportamiento? Tal vez un código de respuesta diferente (probablemente "400 Bad Request")? ¿Qué hay de registrar un error? Tal vez añadir una descripción del problema ( "formato de fecha malo") en un encabezado personalizado para ayudar a solucionar problemas? O devolver una respuesta de error entero con detalles, junto con un código de estado 5xx?

¿Fue útil?

Solución

Existen varios enfoques para personalizar el comportamiento de manejo de errores con JAX-RS. Aquí están tres de las formas más fáciles.

El primer enfoque es crear una clase de excepción que se extiende WebApplicationException.

Ejemplo:

public class NotAuthorizedException extends WebApplicationException {
     public NotAuthorizedException(String message) {
         super(Response.status(Response.Status.UNAUTHORIZED)
             .entity(message).type(MediaType.TEXT_PLAIN).build());
     }
}

Y a tirar esto recién crear Excepción simplemente:

@Path("accounts/{accountId}/")
    public Item getItem(@PathParam("accountId") String accountId) {
       // An unauthorized user tries to enter
       throw new NotAuthorizedException("You Don't Have Permission");
}

Aviso, no es necesario declarar la excepción de una cláusula throws porque WebApplicationException es una excepción de tiempo de ejecución. Esto devolverá una respuesta 401 al cliente.

El segundo y más fácil enfoque es simplemente construir una instancia de la WebApplicationException directamente en el código. Este enfoque funciona siempre y cuando usted no tiene que poner en práctica sus propias excepciones de aplicación.

Ejemplo:

@Path("accounts/{accountId}/")
public Item getItem(@PathParam("accountId") String accountId) {
   // An unauthorized user tries to enter
   throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}

Este código también devuelve un 401 al cliente.

Por supuesto, esto es sólo un ejemplo sencillo. Puede hacer que la Excepción mucho más complejo si es necesario, y se puede generar lo que jamás código de respuesta HTTP es necesario.

Otro enfoque es envolver una excepción existente, tal vez una ObjectNotFoundException con una pequeña clase de contenedor que implementa la interfaz ExceptionMapper anotado con una anotación de @Provider. Esto le indica al tiempo de ejecución JAX-RS, que si se eleva la excepción ajustada, devuelve el código de respuesta definido en el ExceptionMapper.

Otros consejos

@Provider
public class BadURIExceptionMapper implements ExceptionMapper<NotFoundException> {

public Response toResponse(NotFoundException exception){

    return Response.status(Response.Status.NOT_FOUND).
    entity(new ErrorResponse(exception.getClass().toString(),
                exception.getMessage()) ).
    build();
}
}

Crea por encima de la clase. Este se encargará de 404 (NotFoundException) y aquí en el método toResponse le puede dar su respuesta personalizada. Del mismo modo hay ParamException etc., que se necesitaría para mapear para proporcionar respuestas personalizadas.

Jersey lanza una com.sun.jersey.api.ParamException cuando falla en Resolver referencia los parámetros para una solución es crear un ExceptionMapper que se encarga de este tipo de excepciones:

@Provider
public class ParamExceptionMapper implements ExceptionMapper<ParamException> {
    @Override
    public Response toResponse(ParamException exception) {
        return Response.status(Status.BAD_REQUEST).entity(exception.getParameterName() + " incorrect type").build();
    }
}

También puede escribir una clase reutilizable para las variables anotadas-QueryParam

public class DateParam {
  private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

  private Calendar date;

  public DateParam(String in) throws WebApplicationException {
    try {
      date = Calendar.getInstance();
      date.setTime(format.parse(in));
    }
    catch (ParseException exception) {
      throw new WebApplicationException(400);
    }
  }
  public Calendar getDate() {
    return date;
  }
  public String format() {
    return format.format(value.getTime());
  }
}

a continuación, utilizar de esta manera:

private @QueryParam("from") DateParam startDateParam;
private @QueryParam("to") DateParam endDateParam;
// ...
startDateParam.getDate();

Aunque el manejo de errores es trivial en este caso (devolver una respuesta 400), el uso de esta clase permite al factor de salida gestión de parámetros, en general, que podría incluir el registro etc.

Una solución obvia: hay que tomar en una cadena, convertir a Conocer a sí mismo. De esa manera se puede definir el formato que desee, ya sea excepciones de captura y re-lanzamiento o personalizar el error que se envía. Para el análisis, SimpleDateFormat debería funcionar bien.

Estoy seguro de que hay formas de conectar los controladores para los tipos de datos también, pero quizás poco de código simple es todo lo que necesita en este caso.

StaxMan haría probablemente implementar que QueryParam como una cadena, y luego manejar la conversión, Regeneración de que sea necesario.

Si el comportamiento específico de la configuración regional es el comportamiento deseado y esperado, se usaría el siguiente para devolver el error 400 Bad Request:

throw new WebApplicationException(Response.Status.BAD_REQUEST);

Vea el Javadoc de javax.ws.rs.core.Response.Status para más opciones.

documentación @QueryParam dice

  

" El tipo T del parámetro, campo o propiedad anotada debe   o bien:

     

1) Sea un tipo primitivo
  2) Tener un constructor que acepta un único   argumento de cadena
  3) Tener un método estático denominado valueOf o fromstring   que acepta un solo argumento String (ver, por ejemplo,   Integer.valueOf (String))
  4) Tener una aplicación registrada de   javax.ws.rs.ext.ParamConverterProvider JAX-RS extensión SPI que   devuelve una instancia javax.ws.rs.ext.ParamConverter capaz de una "de   cadena" de conversión para el tipo.
  5) Ser List, Set o   SortedSet, donde T satisface 2, 3 o 4 anterior. La resultante   colección es de sólo lectura. "

Si desea controlar qué respuesta va al usuario cuando parámetro de consulta en forma de cadena no se puede convertir a su tipo T, se puede tirar WebApplicationException. Dropwizard viene con * siguientes clases Param se puede utilizar para sus necesidades.

BooleanParam, DateTimeParam, intParam, LongParam, LocalDateParam, NonEmptyStringParam, UUIDParam. Ver https: // github.com/dropwizard/dropwizard/tree/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params

Si necesita Joda DateTime, sólo tiene que utilizar Dropwizard DateTimeParam .

Si la lista anterior no satisface sus necesidades, definir su propio extendiendo AbstractParam. Invalidar método de análisis sintáctico. Si necesita un control sobre el cuerpo respuesta de error, el método de error de anulación.

Buen artículo de Coda Hale en esto es en http: // codahale. com / Jersey-interesante de parámetros-clases-lo que hace /

import io.dropwizard.jersey.params.AbstractParam;

import java.util.Date;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

public class DateParam extends AbstractParam<Date> {

    public DateParam(String input) {
        super(input);
    }

    @Override
    protected Date parse(String input) throws Exception {
        return new Date(input);
    }

    @Override
    protected Response error(String input, Exception e) {
        // customize response body if you like here by specifying entity
        return Response.status(Status.BAD_REQUEST).build();
    }
}

Fecha (String arg) constructor está en desuso. Me gustaría utilizar Java clases fecha 8 si está en Java 8. En caso contrario se recomienda fecha y hora joda.

Este es el comportamiento correcto en realidad. Jersey tratará de encontrar un controlador para su entrada y tratará de construir un objeto desde la entrada proporcionada. En este caso se tratará de crear un nuevo objeto Date con el valor X proporcionada al constructor. Dado que esta es una fecha no válida, por convención Jersey volverá 404.

Lo que puede hacer es volver a escribir y poner la fecha de nacimiento como una cadena, a continuación, tratar de analizar y si no consigue lo que quiere, usted es libre de lanzar cualquier que desee mediante cualquiera de los mecanismos de asignación excepción ( hay varios).

Al igual que una extensión a la respuesta @Steven Lavine en caso de que quiera abrir la ventana del navegador de inicio de sesión. Me resultaba difícil volver adecuadamente la respuesta ( MDN autenticación HTTP ) desde el filtro en caso de que el usuario no se ha autenticado todavía

Esto me ayudó a construir la respuesta a la fuerza de inicio de sesión del navegador, tenga en cuenta la modificación adicional de las cabeceras. Esto establecerá el código de estado 401 y establecer el encabezado que hace que el navegador abra el diálogo de nombre de usuario / contraseña.

// The extended Exception class
public class NotLoggedInException extends WebApplicationException {
  public NotLoggedInException(String message) {
    super(Response.status(Response.Status.UNAUTHORIZED)
      .entity(message)
      .type(MediaType.TEXT_PLAIN)
      .header("WWW-Authenticate", "Basic realm=SecuredApp").build()); 
  }
}

// Usage in the Filter
if(headers.get("Authorization") == null) { throw new NotLoggedInException("Not logged in"); }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top