Ao desenvolver uma API REST com Spring Boot, lidar com erros e exceções de forma adequada é fundamental para garantir uma boa experiência para o consumidor da API. Neste artigo, vamos explorar como tratar exceções personalizadas utilizando @ControllerAdvice
e @ExceptionHandler
, além de mostrar como criar um endpoint GET que responde com status apropriado em caso de erro.
1. O problema: buscar recurso por ID
Quando buscamos um recurso por ID e ele não existe, devemos retornar um erro HTTP 404 – Not Found com uma mensagem amigável e estruturada.
Veja o método abaixo implementado no CategoriaService
:
public CategoriaDTO findById(Long id) {
Optional<Categoria> obj = categoriaRepository.findById(id);
Categoria categoria = obj.orElseThrow(() -> new EntityNotFoundException("Recurso não encontrado."));
return new CategoriaDTO(categoria);
}
Esse método busca a categoria pelo ID e, se não encontrar, lança uma exceção personalizada chamada EntityNotFoundException
.
2. Criando uma exceção personalizada
public class EntityNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public EntityNotFoundException(String msg) {
super(msg);
}
}
Essa exceção estende RuntimeException
e nos permite lançar mensagens customizadas de forma clara e organizada.
3. Criando um handler global com @ControllerAdvice
Em aplicações maiores, onde diversos endpoints e serviços podem lançar exceções, repetir o tratamento de erro em cada controller se torna inviável e propenso a falhas. Para resolver isso, o Spring fornece o conceito de tratador global de exceções, utilizando a anotação @ControllerAdvice
.
Essa anotação transforma a classe em um componente que intercepta todas as exceções lançadas por controllers e permite definir regras de tratamento centralizadas para diferentes tipos de erro.
Veja um exemplo de handler global:
@ControllerAdvice
public class RepositorieExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<StandardError> entityNotFound(EntityNotFoundException e, HttpServletRequest request) {
StandardError error = new StandardError();
error.setTimestamp(Instant.now());
error.setStatus(HttpStatus.NOT_FOUND.value());
error.setError("Recurso não encontrado");
error.setMessage(e.getMessage());
error.setPath(request.getRequestURI());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}
Essa classe intercepta toda exceção do tipo EntityNotFoundException
, constrói uma resposta personalizada (com status, mensagem e detalhes) e retorna um JSON com status 404 Not Found
para o cliente.
4. A classe de erro padronizada: StandardError
public class StandardError implements Serializable {
private static final long serialVersionUID = 1L;
private Instant timestamp;
private Integer status;
private String error;
private String message;
private String path;
// Getters e Setters omitidos para brevidade
}
Essa classe representa um modelo de erro padronizado com os seguintes campos:
timestamp
: Data/hora do errostatus
: Código HTTPerror
: Descrição geral do erromessage
: Mensagem personalizadapath
: Caminho da requisição que gerou o erro
Esse padrão facilita a vida de quem consome a API, pois sempre saberá o formato da resposta de erro.
5. O endpoint GET no Controller
@GetMapping(value = "/{id}")
public ResponseEntity<CategoriaDTO> findById(@PathVariable Long id) {
CategoriaDTO categoriaDTO = categoriaService.findById(id);
return ResponseEntity.ok().body(categoriaDTO);
}
Esse é o endpoint que consome o serviço. Quando a categoria não é encontrada, a exceção é lançada pelo service e automaticamente capturada pelo handler global @ControllerAdvice
.
Conclusão
Com esse padrão, você:
- Mantém a separação entre regras de negócio e tratamento de erro
- Evita duplicar código de captura de exceções em cada controller
- Retorna mensagens de erro mais claras e padronizadas
- Melhora a experiência de consumo da API para o frontend
Esse é um dos pilares para construir APIs REST robustas e profissionais com Spring Boot. Se quiser, podemos aprofundar mais sobre como tratar diferentes tipos de exceções em um próximo artigo. 🚀