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 erro
  • status: Código HTTP
  • error: Descrição geral do erro
  • message: Mensagem personalizada
  • path: 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. 🚀

Categorized in:

Spring Boot,