Estratégias de Gerenciamento de Erros: @ControllerAdvice vs. ErrorPageRegistrar no Spring Boot

Tratamento Centralizado de Exceções com @ControllerAdvice

No desenvolvimento de aplicações Spring Boot, o controle centralizado de exceções é fundamental para manter a robustez e a consistência. A anotação @ControllerAdvice permite agrupar manipuladores de exceção em uma única classe, garantindo que as exceções lançadas por qualquer controlador (dentro dos pacotes configurados) sejam capturadas e processadas de forma padronizada. Isso é especialmente útil para lidar com exceções de tempo de execução e exceções de negócios personalizadas.

A seguir, um exemplo de como configurar uma clasce com @ControllerAdvice para manipular exceções, oferecendo diferentes tipos de resposta, como dados JSON para APIs REST ou redirecionamento para uma página de erro HTML.

package com.example.app.exception;

import com.example.app.util.ApiErrorResponse; // Supondo uma classe de resposta de erro
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

@Slf4j
@ControllerAdvice(basePackages = "com.example.app.controller") // Limita o escopo a pacotes específicos
public class GlobalExceptionHandler {

    /**
     * Manipula exceções e retorna uma resposta JSON para chamadas de API.
     * Define o status HTTP como 500 (Internal Server Error).
     * @param excecao A exceção capturada.
     * @return Um objeto ApiErrorResponse contendo detalhes do erro.
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiErrorResponse handleApiRequestException(Exception excecao) {
        log.error("Ocorreu uma exceção durante o processamento da requisição: {}", excecao.getMessage(), excecao);
        return new ApiErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Erro interno no servidor. Tente novamente mais tarde.");
    }

    /**
     * Manipula exceções e redireciona para uma página de erro HTML.
     * Define o status HTTP como 500 (Internal Server Error).
     * @param excecao A exceção capturada.
     * @return O nome da view para a página de erro 500.
     */
    @ExceptionHandler(value = RuntimeException.class) // Pode ser mais específico para RuntimeException
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String handleMvcRequestException(RuntimeException excecao) {
        log.error("Exceção de tempo de execução capturada, redirecionando para página de erro: {}", excecao.getMessage(), excecao);
        return "error/server-error"; // Nome da view (e.g., src/main/resources/templates/error/server-error.html)
    }
}

Note que para o exemplo acima funcionar, você precisaria de uma classe ApiErrorResponse simples, por exemplo:

package com.example.app.util;

public class ApiErrorResponse {
    private int status;
    private String message;

    public ApiErrorResponse(int status, String message) {
        this.status = status;
        this.message = message;
    }

    public int getStatus() { return status; }
    public String getMessage() { return message; }
    // Getters e Setters
}

Configuração de Páginas de Erro com ErrorPageRegistrar

O Spring Boot oferece uma maneira flexível de configurar páginas de erro personalizadas para diferentes códigos de status HTTP através da interface ErrorPageRegistrar. Essa abordagme é ideal para lidar com erros de nível de servidor que podem ocorrer antes mesmo de uma exceção ser lançada pela lógica da aplicação, como um erro 404 (recurso não encontrado) ou 500 (erro interno do servidor) detectado pelo próprio contêiner web. Ao implementar essa interface, você pode mapear códigos de status HTTP para URLs específicas que servirão as páginas de erro.

package com.example.app.config;

import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

/**
 * Registra páginas de erro personalizadas para diferentes códigos de status HTTP.
 */
@Component
public class CustomErrorPageConfig implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        // Mapeia o status 404 (Não Encontrado) para o caminho /errors/page-not-found
        ErrorPage notFoundErrorPage = new ErrorPage(HttpStatus.NOT_FOUND, "/errors/page-not-found");
        
        // Mapeia o status 500 (Erro Interno do Servidor) para o caminho /errors/internal-server-error
        ErrorPage serverErrorPage = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/errors/internal-server-error");
        
        registry.addErrorPages(notFoundErrorPage, serverErrorPage);
    }
}

Para que as URLs definidas na configuração acima funcionem, você precisa de um controlador que lide com essas requisições e retorne as views apropriadas:

package com.example.app.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Controlador para servir as páginas de erro configuradas.
 */
@Controller
@RequestMapping("/errors")
public class ErrorPageController {

    @RequestMapping("/page-not-found")
    public String render404Page() {
        return "error/404-page"; // Mapeia para src/main/resources/templates/error/404-page.html
    }

    @RequestMapping("/internal-server-error")
    public String render500Page() {
        return "error/500-page"; // Mapeia para src/main/resources/templates/error/500-page.html
    }
}

Comparação e Prioridade

Ambas as abordagens, @ControllerAdvice e ErrorPageRegistrar, servem para gerenciar erros em aplicações Spring Boot, mas têm escopos e características distintas:

  • @ControllerAdvice:
    • É excelente para capturar exceções lançadas pela lógica da aplicação, incluindo exceções personalizadas e aquelas que resultam em erros 5xx (como NullPointerException, IOException, etc.).
    • Permite retornar respostas JSON (para APIs) ou redirecionar para views (para aplicações MVC).
    • Não consegue capturar erros de nível HTTP que ocorrem antes da requisição chegar aos controladores da aplicação, como erros 404 de URLs inválidas.
  • ErrorPageRegistrar:
    • É ideal para lidar com erros de status HTTP gerais, como 404 (recurso não encontrado), 500 (erro interno do servidor) e outros.
    • Funciona bem para interceptar erros que o contêiner web detecta, mesmo antes que qualquer controlador seja invocado.
    • Pode redirecionar para URLs que podem servir tanto páginas HTML quanto respostas JSON, dependendo do controlador mapeado.
    • Não é adequado para capturar exceções personalizadas da aplicação, pois ele se concentra em códigos de status HTTP.

É importante notar que, se ambos @ControllerAdvice e ErrorPageRegistrar estiverem configurados para lidar com o mesmo tipo de erro (por exemplo, um erro 500), o @ControllerAdvice terá prioridade. Isso significa que, se uma exceção que resulta em um 500 for lançada por um controlador, o manipulador em @ControllerAdvice será acionado primeiro, e a configuração de ErrorPageRegistrar para 500 não será utilizada, a menos que o @ControllerAdvice não consiga capturar ou decidir não processar a exceção.

Tags: Spring Boot Exception Handling ControllerAdvice ErrorPageRegistrar HTTP Status Codes

Publicado em 6-29 23:44