Como retornar 404 com Spring WebFlux
1. Visão geral
Com o Spring Boot 2 e o novo servidor sem bloqueio Netty, não temos mais a API de contexto do Servlet, então vamos discutir como podemos expressar diferentes tipos de códigos de status HTTP, usando a nova pilha.
2. Status de resposta semântica
Siga a prática RESTful padrão, naturalmente precisamos fazer uso de toda a gama de códigos de status HTTP para expressar a semântica da API corretamente.
2.1. Status de retorno padrão
Obviamente, quando tudo corre bem, o status de resposta padrão é 200 (OK) :
@GetMapping(
value = "/ok",
produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
public Flux<String> ok() {
return Flux.just("ok");
}
2.2. Usando anotações
Podemos alterar o status de retorno padrão adicionando a anotação _ @ ResponseStatus_ ao método:
@GetMapping(
value = "/no-content",
produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
@ResponseStatus(HttpStatus.NO_CONTENT)
public Flux<String> noContent() {
return Flux.empty();
}
2.3. Alterando o status programaticamente
Em alguns casos, dependendo do comportamento do servidor, poderíamos decidir alterar o status retornado programaticamente em vez de um status retornado prefixado usado por padrão ou com anotações.
Podemos conseguir essa injeção ServerHttpResponse no nosso método diretamente:
@GetMapping(
value = "/accepted",
produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
public Flux<String> accepted(ServerHttpResponse response) {
response.setStatusCode(HttpStatus.ACCEPTED);
return Flux.just("accepted");
}
Agora podemos escolher qual código de status HTTP retornamos diretamente na implementação.
2.4. Lançando uma exceção
Sempre que lançamos uma exceção, o status de retorno HTTP padrão é omitido e o Spring tenta encontrar um manipulador de exceções para lidar com isso:
@GetMapping(
value = "/bad-request"
)
public Mono<String> badRequest() {
return Mono.error(new IllegalArgumentException());
}
@ResponseStatus(
value = HttpStatus.BAD_REQUEST,
reason = "Illegal arguments")
@ExceptionHandler(IllegalArgumentException.class)
public void illegalArgumentHandler() {
//
}
Para saber mais sobre como fazer isso, verifique definitivamente o https://www..com/exception-handling-for-rest-with-spring [Artigo sobre manipulação de erros no Baeldung].
2.5. Com ResponseEntity
Vamos agora dar uma olhada rápida em uma alternativa interessante - a classe ResponseEntity.
Isso nos permite escolher qual status HTTP queremos retornar e também personalizar ainda mais nossas respostas, usando uma API fluente muito útil:
@GetMapping(
value = "/unauthorized"
)
public ResponseEntity<Mono<String>> unathorized() {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.header("X-Reason", "user-invalid")
.body(Mono.just("unauthorized"));
}
2.6. Com terminais funcionais
Com o Spring 5, podemos definir pontos de extremidade de maneira funcional, para que também possamos alterar o status HTTP padrão programaticamente:
@Bean
public RouterFunction<ServerResponse> notFound() {
return RouterFunctions
.route(GET("/statuses/not-found"),
request -> ServerResponse.notFound().build());
}
3. Conclusão
Ao implementar uma API HTTP, a estrutura oferece várias opções para lidar de maneira inteligente com os códigos de status que estamos expondo de volta ao cliente.
Este artigo deve ser um bom ponto de partida para explorá-los e entender como você pode implementar uma API amigável e expressiva, com semântica limpa e RESTful.
Obviamente, os exemplos de código completos usados neste tutorial estão disponíveis over no Github.