Existem vários casos de uso para definir códigos de status HTTP em um serviço da Web REST, e pelo menos um não foi suficientemente documentado nas respostas existentes (por exemplo, quando você está usando a serialização JSON / XML auto-mágica usando JAXB e deseja retornar um objeto a ser serializado, mas também um código de status diferente do padrão 200).
Então, deixe-me tentar enumerar os diferentes casos de uso e as soluções para cada um:
1. Código de erro (500, 404, ...)
O caso de uso mais comum em que você deseja retornar um código de status diferente 200 OK
daquele em que ocorre um erro.
Por exemplo:
- uma entidade é solicitada, mas ela não existe (404)
- a solicitação está semanticamente incorreta (400)
- o usuário não está autorizado (401)
- há um problema com a conexão com o banco de dados (500)
- etc ..
a) Lance uma exceção
Nesse caso, acho que a maneira mais limpa de lidar com o problema é lançar uma exceção. Essa exceção será tratada por um ExceptionMapper
, que converterá a exceção em uma resposta com o código de erro apropriado.
Você pode usar o padrão ExceptionMapper
que vem pré-configurado com Jersey (e acho que é o mesmo com outras implementações) e lançar qualquer uma das subclasses existentes de javax.ws.rs.WebApplicationException
. Estes são tipos de exceção predefinidos, pré-mapeados para diferentes códigos de erro, por exemplo:
- BadRequestException (400)
- InternalServerErrorException (500)
- NotFoundException (404)
Etc. Você pode encontrar a lista aqui: API
Como alternativa, você pode definir suas próprias exceções e ExceptionMapper
classes personalizadas e adicionar esses mapeadores a Jersey pela média da @Provider
anotação ( fonte deste exemplo ):
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);
}
}
Fornecedor :
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Nota: você também pode escrever ExceptionMappers para os tipos de exceção existentes que você usa.
b) Use o construtor Response
Outra maneira de configurar um código de status é usar um Response
construtor para construir uma resposta com o código pretendido.
Nesse caso, o tipo de retorno do seu método deve ser javax.ws.rs.core.Response
. Isso é descrito em várias outras respostas, como a resposta aceita pela desistência e se parece com isso:
@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. Sucesso, mas não 200
Outro caso em que você deseja definir o status de retorno é quando a operação foi bem-sucedida, mas você deseja retornar um código de sucesso diferente de 200, juntamente com o conteúdo que você retorna no corpo.
Um caso de uso frequente é quando você cria uma nova entidade ( POST
solicitação) e deseja retornar informações sobre essa nova entidade ou talvez a própria entidade, junto com um 201 Created
código de status.
Uma abordagem é usar o objeto de resposta como descrito acima e definir o corpo da solicitação. No entanto, ao fazer isso, você perde a capacidade de usar a serialização automática para XML ou JSON fornecida pelo JAXB.
Este é o método original que retorna um objeto de entidade que será serializado em JSON por JAXB:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
Isso retornará uma representação JSON do usuário recém-criado, mas o status de retorno será 200, não 201.
Agora, o problema é que, se eu quiser usar o Response
construtor para definir o código de retorno, tenho que retornar um Response
objeto no meu método. Como ainda retorno o User
objeto a ser serializado?
a) Defina o código na resposta do servlet
Uma abordagem para resolver isso é obter um objeto de solicitação de servlet e definir o código de resposta manualmente, como demonstrado na resposta 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;
}
O método ainda retorna um objeto de entidade e o código de status será 201.
Observe que, para fazê-lo funcionar, tive que liberar a resposta. Esse é um ressurgimento desagradável do código da API do Servlet de baixo nível em nosso bom recurso JAX_RS e, muito pior, faz com que os cabeçalhos não sejam modificáveis depois disso, porque eles já foram enviados pela conexão.
b) Use o objeto de resposta com a entidade
A melhor solução, nesse caso, é usar o objeto Response e configurar a entidade para ser serializada nesse objeto de resposta. Seria bom tornar o objeto Response genérico para indicar o tipo de entidade de carga útil nesse caso, mas atualmente não é o caso.
@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();
}
Nesse caso, usamos o método criado da classe do construtor Response para definir o código de status como 201. Passamos o objeto de entidade (usuário) para a resposta por meio do método entity ().
O resultado é que o código HTTP é 401 como desejávamos, e o corpo da resposta é exatamente o mesmo JSON que tínhamos antes, quando retornamos o objeto Usuário. Ele também adiciona um cabeçalho de localização.
A classe Response possui vários métodos do construtor para diferentes status (stati?), Como:
Response.accepted () Response.ok () Response.noContent () Response.notAcceptable ()
NB: o objeto hateoas é uma classe auxiliar que desenvolvi para ajudar a gerar URIs de recursos. Você precisará criar seu próprio mecanismo aqui;)
É sobre isso.
Espero que esta longa resposta ajude alguém :)