2011-06-29 8 views
15

Con mi servicio JAX-RS muy simple, estoy usando Tomcat con el dominio JDBC para la autenticación, por lo tanto, estoy trabajando con las anotaciones JSR 250.Respuesta de estado HTTP personalizada con JAX-RS (Jersey) y @RolesAllowed

Lo que pasa es que quiero devolver un cuerpo de mensaje personalizado en la respuesta de estado HTTP. El código de estado (403) debe permanecer igual. Por ejemplo, mi servicio tiene el siguiente aspecto:

@RolesAllowed({ "ADMIN" }) 
@Path("/users") 
public class UsersService { 

    @GET 
    @Produces(MediaType.TEXT_PLAIN) 
    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) 
    public String getUsers() { 
     // get users ... 
     return ...; 
    } 
} 

Si un usuario con una función diferente de "admin" acceder al servicio, quiero cambiar el mensaje de respuesta a algo así (dependiendo del tipo de medio [ XML/JSON]):

<error id="100"> 
    <message>Not allowed.</message> 
</error> 

Por el momento Jersey devuelve el siguiente cuerpo:

HTTP Status 403 - Forbidden 

type Status report 
message Forbidden 
description Access to the specified resource (Forbidden) has been forbidden. 
Apache Tomcat/7.0.12 

¿Cómo puedo cambiar el cuerpo del mensaje por defecto? ¿Hay alguna forma de manejar la excepción (tal vez lanzada) para construir mi propia respuesta de estado HTTP?

+0

Un ejemplo completo: https://www.bhaveshthaker.com/25/customize-handling-server-side-exceptions-with-error-codes-using-exceptionmapper-with-jersey-jax-rs-in-java/ – RAS

Respuesta

17

La manera más fácil de manejar este tipo de cosas es lanzar una excepción y registrar un asignador de excepciones para convertirlo en el tipo de mensaje que desea enviar en ese caso. Así, supongamos que usted lanza una AccessDeniedException, entonces tendría un controlador como este (con nombres de clase completos en lugares para mayor claridad):

@javax.ws.rs.ext.Provider 
public class AccessDeniedHandler 
     implements javax.ws.rs.ext.ExceptionMapper<AccessDeniedException> { 
    public javax.ws.rs.core.Response toResponse(AccessDeniedException exn) { 
     // Construct+return the response here... 
     return Response.status(403).type("text/plain") 
       .entity("get lost, loser!").build(); 
    } 
} 

La forma en que se registra el asignador excepción varía de acuerdo con el marco que' reutilizando, pero para Jersey deberías estar bien con solo usar @Provider. Dejaré que descubras por ti mismo cómo quieres generar el tipo de documentos de error que deseas, pero recomiendo manejar fallas como códigos de error HTTP de algún tipo (eso es más RESTful ...)

+0

Lo que pasa es que Jersey devuelve su respuesta personalizada 403. No puedo lanzar la 'AccessDeniedException' para autorización ya que las cosas de @RolesAllowed manejan esto. El código de error HTTP debería ser el único que quiero cambiar el mensaje del cuerpo. – Stefan

+0

Pensé que podrías hacer eso en Jersey con un ExceptionMapper; Sé que puedes hacerlo con Apache CXF (aunque está configurado de manera diferente por razones que he llegado a apreciar), así que creí que era razonable al menos _esto_ dado que ambos son motores JAX-RS y esto solo está usando la API JAX-RS , pero no tengo experiencia directa con Jersey así que tal vez haya algo inesperado en el camino. –

9

Con la creación un ExceptionMapper (excepciones mapeo de WebApplicationException) es posible "atrapar" ciertas excepciones producidas por la aplicación:

@Provider 
public class MyExceptionMapper implements ExceptionMapper<WebApplicationException> { 

    @Override 
    public Response toResponse(WebApplicationException weException) { 

     // get initial response 
     Response response = weException.getResponse(); 

     // create custom error 
     MyError error = ...; 

     // return the custom error 
     return Response.status(response.getStatus()).entity(error).build(); 
    } 
} 

también es necesario añadir el paquete a su web.xml solicitud de registro del proveedor:

<init-param> 
    <param-name>com.sun.jersey.config.property.packages</param-name> 
    <param-value> 
     com.myapp.userservice; // semi-colon seperated 
     com.myapp.mappedexception 
    </param-value> 
</init-param> 
+0

Estaba enfrentando el mismo problema y en mi caso esa fue la respuesta perfecta. Gracias Stefan! – Lars

1

REST se basa en HTTP, por lo que no tiene que cambiar el comportamiento predeterminado de un error de autenticación. Tener un error 403 al acceder a un recurso es suficiente para que el cliente entienda claramente lo que se agrega.

Cuantos más recursos cumplan con HTTP, más podrán entenderlo otros.

+2

el problema es que puede haber varias situaciones en las que aparece un solo error HTTP.Uno puede ser un tiempo de espera de sesión (resultados en 403) administrado por el AS. Otro puede ser un intento no autorizado de un recurso de descanso (403). Si tiene que reaccionar programáticamente de diferentes maneras sobre esos errores, es una buena forma de personalizarlos. – Lars

Cuestiones relacionadas