Hay varios casos de uso para configurar códigos de estado HTTP en un servicio web REST, y al menos uno no estaba suficientemente documentado en las respuestas existentes (es decir, cuando está utilizando la serialización JSON / XML auto-mágica utilizando JAXB, y desea devolver un objeto para ser serializado, pero también un código de estado diferente al predeterminado 200).
Permítanme intentar enumerar los diferentes casos de uso y las soluciones para cada uno:
1. Código de error (500, 404, ...)
El caso de uso más común cuando desea devolver un código de estado diferente de 200 OK
cuando ocurre un error.
Por ejemplo:
- se solicita una entidad pero no existe (404)
- la solicitud es semánticamente incorrecta (400)
- el usuario no está autorizado (401)
- hay un problema con la conexión de la base de datos (500)
- etc.
a) Lanzar una excepción
En ese caso, creo que la forma más limpia de manejar el problema es lanzar una excepción. Esta excepción será manejada por un ExceptionMapper
, que traducirá la excepción en una respuesta con el código de error apropiado.
Puede usar el valor predeterminado ExceptionMapper
que viene preconfigurado con Jersey (y supongo que es lo mismo con otras implementaciones) y arrojar cualquiera de las subclases existentes de javax.ws.rs.WebApplicationException
. Estos son tipos de excepción predefinidos que se asignan previamente a diferentes códigos de error, por ejemplo:
- BadRequestException (400)
- InternalServerErrorException (500)
- NotFoundException (404)
Etc. Puede encontrar la lista aquí: API
Alternativamente, puede definir sus propias excepciones y ExceptionMapper
clases personalizadas, y agregar estos mapeadores a Jersey por medio de la @Provider
anotación ( fuente de este ejemplo ):
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);
}
}
Proveedor :
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Nota: también puede escribir ExceptionMappers para los tipos de excepción existentes que usa.
b) Use el generador de respuestas
Otra forma de establecer un código de estado es usar un Response
generador para generar una respuesta con el código deseado.
En ese caso, el tipo de retorno de su método debe ser javax.ws.rs.core.Response
. Esto se describe en varias otras respuestas, como la respuesta aceptada de hisdrewness y se ve así:
@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. Éxito, pero no 200
Otro caso en el que desea establecer el estado de retorno es cuando la operación fue exitosa, pero desea devolver un código de éxito diferente de 200, junto con el contenido que devuelve en el cuerpo.
Un caso de uso frecuente es cuando crea una nueva entidad ( POST
solicitud) y desea devolver información sobre esta nueva entidad o tal vez la entidad misma, junto con un 201 Created
código de estado.
Un enfoque es utilizar el objeto de respuesta tal como se describió anteriormente y establecer el cuerpo de la solicitud usted mismo. Sin embargo, al hacer esto, pierde la capacidad de utilizar la serialización automática a XML o JSON proporcionada por JAXB.
Este es el método original que devuelve un objeto de entidad que JAXB serializará a JSON:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
Esto devolverá una representación JSON del usuario recién creado, pero el estado de devolución será 200, no 201.
Ahora el problema es que si quiero usar el Response
generador para configurar el código de retorno, tengo que devolver un Response
objeto en mi método. ¿Cómo devuelvo el User
objeto para que se serialice?
a) Establezca el código en la respuesta del servlet
Un enfoque para resolver esto es obtener un objeto de solicitud de servlet y establecer el código de respuesta manualmente nosotros mismos, como se demuestra en la respuesta 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;
}
El método aún devuelve un objeto de entidad y el código de estado será 201.
Tenga en cuenta que para que funcione, tuve que vaciar la respuesta. Este es un resurgimiento desagradable del código API de Servlet de bajo nivel en nuestro agradable recurso JAX_RS, y mucho peor, hace que los encabezados no se puedan modificar después de esto porque ya se enviaron por cable.
b) Use el objeto de respuesta con la entidad
La mejor solución, en ese caso, es usar el objeto de Respuesta y configurar la entidad para que se serialice en este objeto de respuesta. Sería bueno hacer que el objeto Respuesta sea genérico para indicar el tipo de entidad de carga útil en ese caso, pero no es el caso actual.
@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();
}
En ese caso, utilizamos el método creado de la clase de generador de Respuesta para establecer el código de estado en 201. Pasamos el objeto de entidad (usuario) a la respuesta a través del método de entidad ().
El resultado es que el código HTTP es 401 como queríamos, y el cuerpo de la respuesta es exactamente el mismo JSON que teníamos antes cuando acabamos de devolver el objeto Usuario. También agrega un encabezado de ubicación.
La clase Response tiene varios métodos de creación para diferentes estados (stati?) Como:
Response.accepted () Response.ok () Response.noContent () Response.notAcceptable ()
NB: el objeto hateoas es una clase auxiliar que desarrollé para ayudar a generar recursos URI. Tendrá que crear su propio mecanismo aquí;)
Eso es todo.
Espero que esta larga respuesta ayude a alguien :)