Sistema de Encurtamento de URLs: Implementação de Gerenciamento de Exceções e Tratamento Global

Criar um diretório chamado exception dentro do diretório convention, e adicionar as classes AbstractException, ClientException, ServiceException e RemoteException.

/**
 * Sistema abstrato de exceções para o projeto com três categorias: 
 * exceções do cliente, exceções do servidor e exceções de chamadas remotas
 *
 * @see ClientException
 * @see ServiceException  
 * @see RemoteException
 */
@Getter
public abstract class AbstractException extends RuntimeException {
    public final String codigoErro;
    public final String mensagemErro;

    public AbstractException(String mensagem, Throwable causa, ICodigoErro codigoErro) {
        super(mensagem, causa);
        this.codigoErro = codigoErro.codigo();
        this.mensagemErro = Optional.ofNullable(StringUtils.hasLength(mensagem) ? mensagem : null)
                .orElse(codigoErro.mensagem());
    }
}

/**
 * Exceção do cliente
 */
public class ClientException extends AbstractException {
    public ClientException(ICodigoErro codigoErro) {
        this(null, null, codigoErro);
    }

    public ClientException(String mensagem) {
        this(mensagem, null, BaseErrorCode.CLIENT_ERROR);
    }

    public ClientException(String mensagem, ICodigoErro codigoErro) {
        this(mensagem, null, codigoErro);
    }

    public ClientException(String mensagem, Throwable causa, ICodigoErro codigoErro) {
        super(mensagem, causa, codigoErro);
    }

    @Override
    public String toString() {
        return "ClientException{" +
                "codigo='" + codigoErro + "'," +
                "mensagem='" + mensagemErro + "'" +
                '}';
    }
}

/**
 * Exceção de chamada remota
 */
public class RemoteException extends AbstractException {
    public RemoteException(String mensagem) {
        this(mensagem, null, BaseErrorCode.REMOTE_ERROR);
    }

    public RemoteException(String mensagem, ICodigoErro codigoErro) {
        this(mensagem, null, codigoErro);
    }

    public RemoteException(String mensagem, Throwable causa, ICodigoErro codigoErro) {
        super(mensagem, causa, codigoErro);
    }

    @Override
    public String toString() {
        return "RemoteException{" +
                "codigo='" + codigoErro + "'," +
                "mensagem='" + mensagemErro + "'" +
                '}';
    }
}

/**
 * Exceção do serviço
 */
public class ServiceException extends AbstractException {
    public ServiceException(String mensagem) {
        this(mensagem, null, BaseErrorCode.SERVICE_ERROR);
    }

    public ServiceException(ICodigoErro codigoErro) {
        this(null, codigoErro);
    }

    public ServiceException(String mensagem, ICodigoErro codigoErro) {
        this(mensagem, null, codigoErro);
    }

    public ServiceException(String mensagem, Throwable causa, ICodigoErro codigoErro) {
        super(Optional.ofNullable(mensagem).orElse(codigoErro.mensagem()), causa, codigoErro);
    }

    @Override
    public String toString() {
        return "ServiceException{" +
                "codigo='" + codigoErro + "'," +
                "mensagem='" + mensagemErro + "'" +
                '}';
    }
}

O UserServiceImpl pode ser modificado da seguinte forma:

O trecho de código implementa um método getUserByUsername da interface UserService, responsável por obter informações do usuário com base no nome de usuário e retornar um objeto UserRespDTO. O método inclui uma verificação condicional if (usuarioDO == null) para tratar o caso em que o usuário não existe (quando o objeto usuarioDO é nulo, indica que não há registros correspondentes no banco de dados).

Caso a verificação if (usuarioDO == null) seja removida, quando não for encontrado nenhum registro correspondente no banco de dados, a variável usuarioDO será atribuída como nula. A subsequente chamada BeanUtils.copyProperties(usuarioDO, resultado) falhará, pois o método tentará acessar as propriedades de usuarioDO para copiar para o objeto resultado. Se usuarioDO for nulo, isso causará uma NullPointerException.

Criar a classe Results no diretório result para simplificar o UserController.

No diretório common, criar um subdiretório web e adicionar o GlobalExceptionHandler. Este interceptador global de exceções é necessário porque é impossível capturar todas as exceções na lógica do código. Por exemplo, se esquecermos de escrever return null, ocorrerá uma exceção de ponteiro nulo. Nesse momento, o interceptador pode capturar esses erros e rpeortar as exceções.

/**
 * Manipulador global de exceções
 *
 */
@Component
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * Intercepta exceções de validação de parâmetros
     */
    @SneakyThrows
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result validacaoExcecaoHandler(HttpServletRequest requisicao, MethodArgumentNotValidException ex) {
        BindingResult resultadoVinculo = ex.getBindingResult();
        FieldError primeiroErroCampo = CollectionUtil.getFirst(resultadoVinculo.getFieldErrors());
        String strExcecao = Optional.ofNullable(primeiroErroCampo)
                .map(FieldError::getDefaultMessage)
                .orElse(StrUtil.EMPTY);
        log.error("[{}] {} [ex] {}", requisicao.getMethod(), getUrl(requisicao), strExcecao);
        return Results.falha(BaseErrorCode.CLIENT_ERROR.codigo(), strExcecao);
    }

    /**
     * Intercepta exceções lançadas na aplicação
     */
    @ExceptionHandler(value = { AbstractException.class })
    public Result excecaoAbstrata(HttpServletRequest requisicao, AbstractException ex) {
        if (ex.getCause() != null) {
            log.error("[{}] {} [ex] {}", requisicao.getMethod(), requisicao.getRequestURL().toString(), ex.toString(),
                    ex.getCause());
            return Results.falha(ex);
        }
        log.error("[{}] {} [ex] {}", requisicao.getMethod(), requisicao.getRequestURL().toString(), ex.toString());
        return Results.falha(ex);
    }

    /**
     * Intercepta exceções não capturadas
     */
    @ExceptionHandler(value = Throwable.class)
    public Result manipuladorErroPadrao(HttpServletRequest requisicao, Throwable throwable) {
        log.error("[{}] {} ", requisicao.getMethod(), getUrl(requisicao), throwable);
        return Results.falha();
    }

    private String getUrl(HttpServletRequest requisicao) {
        if (StringUtils.isEmpty(requisicao.getQueryString())) {
            return requisicao.getRequestURL().toString();
        }
        return requisicao.getRequestURL().toString() + "?" + requisicao.getQueryString();
    }
}

Tags: java spring-boot exception-handling global-exception-handler short-url-service

Publicado em 8-3 22:15