Tratamento adequado de exceções Java

        Vamos falar sobre pontos problemáticos. Devido às minhas responsabilidades, tenho que utilizar muitos serviços diferentes (fazer edição, fazer revisão de código...); equipes diferentes costumam escrever todos esses serviços, sempre que se trata de tratar erros e encaminhá-los do serviço, às vezes meus olhos começavam para água. Deixe-me tentar dizer qual código eu acho que tem tratamento de erros inaceitável e como acho que deveria ser tratado.

        Normalmente, inicialmente o problema está oculto em uma falha na análise do serviço. Em geral, não há nenhum requisito de referência sobre como os erros são lançados. Em geral, isso acontece por dois motivos: o primeiro é a pressa em desenvolver novos serviços e o segundo é que os analistas confiam na experiência dos desenvolvedores. Nesse caso, o analista simplesmente diz ao desenvolvedor: "Ok, dê-me um exemplo de mensagem de erro mais tarde e irei anexá-la no Confluence."

        Passemos ao exemplo, vejamos as consequências desta abordagem no desenvolvimento.

A primeira coisa a não fazer é lançar uma RuntimeException:

public Object badExample() {
  throw new RuntimeException("Something wrong!");
}

Exibir:

 

Um serviço ou cliente chamador receberá um erro 500 e não terá como entender o que há de errado com sua solicitação. Quanto tempo você acha que levará para encontrar o problema neste caso? Ele crescerá proporcionalmente com a quantidade do seu código. Se você tiver uma cadeia de chamadas de métodos, será mais difícil lidar com essas mensagens quando os métodos forem chamados dentro do serviço.

Como melhorar isso? Primeiro, vamos criar um manipulador de erros; quase todos os frameworks fornecem isso imediatamente; no meu exemplo, usarei o manipulador do framework Spring.

exemplo:

@ControllerAdvice
public class ApiExceptionHandler {

  @ExceptionHandler(value = {RuntimeException.class})
  public ResponseEntity<Object> handler(RuntimeException e) {
    HttpStatus badRequest = HttpStatus.INTERNAL_SERVER_ERROR;
    return new ResponseEntity<>(e.getMessage(), badRequest);
  }
  ...

Exibir:

 

Agora vemos a mensagem, mas o formato da mensagem é uma string, não JSON. Vamos consertar isso em breve.

        Idealmente, todos os serviços em um projeto deveriam ter o mesmo formato de mensagem de erro. Pelo menos dois campos são a própria mensagem e um código de erro inteiro interno. Acho que não há necessidade de explicar por que o texto da mensagem é insuficiente e quantos recursos são necessários para processar essa sequência.

exemplo:

public class ExampleApiError {

  private String message;
  private Integer code;
  private LocalDateTime dateTime;
  ...

        Preenchemos esta classe em nosso manipulador de erros. Mas como eu disse, lançar RuntimeException é uma má prática, então você precisa criar sua própria classe para lançar o erro, no construtor desta classe passaremos uma mensagem e um código de erro.

public class ApiException extends RuntimeException {

  private final int code;

  public ApiException(String msg, int code) {
    super(msg);
    this.code = code;
  }
  ...

Tudo parece estar mais claro, mas mesmo aqui começam os problemas: cada um cria os parâmetros passados ​​para o construtor da classe de forma diferente. Alguns criam mensagens e códigos de erro diretamente nos métodos.

exemplo:

public Object badExample() {
  throw new ApiException("Something wrong!", 10);
}

Encontrar esses locais no código ainda é difícil. Bom, o primeiro pensamento foi criar classes constantes, uma classe para mensagens e uma segunda classe para códigos de mensagens.

exemplo:

public static final String SOMETHING_WRONG = "Something wrong";
public static final int SOMETHING_WRONG_CODE = 10;

public Object badExample() {
  throw new ApiException(SOMETHING_WRONG, SOMETHING_WRONG_CODE);
}

        Isso já é melhor, mais legível e mais fácil de encontrar quando você sabe onde está o código do erro.

        No entanto, se você não tiver microsserviços, talvez não seja uma boa ideia armazenar tudo em uma classe. As mensagens começam a ficar maiores, então é melhor separar as classes constantes com base na funcionalidade dos métodos que serão usados ​​com ProductExceptionConstant, PaymentExceptionConstant, etc.

        Mas isso não é tudo. Para alguns, isso pode parecer desatualizado, mas as constantes precisam ser criadas como enumerações ou interfaces. As variáveis ​​nas interfaces são estáticas, públicas e finais por padrão. Não sou contra isso; o ponto principal é que tudo deve ser uniforme; se você começar a fazer coisas através de interfaces, continue fazendo; não há necessidade de mixagem. Em um dos projetos, vi três métodos constantes diferentes usados ​​pela mesma equipe. Você não precisa. )

        Mostrarei mais um exemplo de projeto real que chamou minha atenção durante o processo de revisão.

        Primeiro, o desenvolvedor decidiu que todas as entidades que ele retornar conterão mensagens e códigos de erro, e o status será sempre 200, o que, na minha opinião, engana o chamador. Bem, um exemplo de constante.

public enum ErrorsEnum {
    DEFAULT_ERROR(ErrorsConstants.DEFAULT_ERROR, "400"),
    REFRESH_TOKEN_NOT_VALID(ErrorsConstants.REFRESH_TOKEN_NOT_VALID, "400.1"),
    USER_NOT_FOUND(ErrorsConstants.USER_NOT_FOUND, "400.2"),

Parece-me que falta o código 9 3\4. Por padrão, neste caso, você pode usar o Pi digital.

        Se você está interessado em meu método, vá em frente. O mais conveniente a fazer é criar uma interface que seja um contrato vinculativo para todos.

public interface ExceptionBase {

  String getMsg();

  Integer getCode();
}

E o construtor da classe de exceção precisa ser alterado.

public ApiException(ExceptionBase e) {
  super(e.getMsg());
  this.code = e.getCode();
}

Agora, todos que tentarem lançar uma exceção entenderão que temos que passar um objeto que implementa a interface para o construtor. A escolha mais adequada é Enum.

exemplo:

/**
 * This class is intended for declaring exceptions that occur during order processing. code range
 * 101-199.
 */
public enum OrderException implements ExceptionBase {
  ORDER_NOT_FOUND("Order not found.", 101),
  ORDER_CANNOT_BE_UPDATED("Order cannot be updated,", 102);

  OrderException(String msg, Integer code) {
    this.msg = msg;
    this.code = code;
  }

  private final String msg;
  private final Integer code;

  @Override
  public String getMsg() {
    return msg;
  }

  @Override
  public Integer getCode() {
    return code;
  }
}

Exemplo de uso:

public Object getProduct(String id) {
  if (id.equals("-1")) {
    throw new ApiException(OrderException.ORDER_NOT_FOUND);
  }
...
 

Resposta do servidor:

 Portanto, temos o seguinte modelo de saco de exceção.

 

Como você pode ver, nada complicado, a lógica é fácil de ler. Neste exemplo, deixei um construtor na classe ApiException que aceita uma string, mas para maior confiabilidade é melhor removê-lo.

Normalmente, a maioria das inconsistências no código se deve à falta de inspeções de código ou a inspeções inadequadas. A desculpa mais comum é: “Esta é uma solução temporária; vamos consertar isso mais tarde”, mas não, não funciona assim; ninguém se preocupa em descobrir onde existe uma solução temporária e onde existe uma solução permanente . Acontece que “o que é temporário é o que é permanente”.

Se você tiver muitos serviços se comunicando entre si, simplificará bastante seu trabalho de escrever bibliotecas clientes criando um formato de mensagem de erro. Por exemplo, ao usar o Retrofit, uma vez escrito o núcleo do manipulador, você só precisa alterar o método na interface e o objeto que ele recebe.

para concluir

O tratamento de erros é uma parte muito importante do seu código; facilita a localização de áreas problemáticas no seu código e também permite que clientes externos entendam o que estão fazendo de errado quando usam um endpoint, portanto, você deve estar certo desde o início. primeira etapa de redação do seu projeto. A devida atenção é dada a isso.

Exemplo de código aqui .

Espero que sua vida seja um pouco mais fácil depois de ler este artigo. Tudo funcionou.

Acho que você gosta

Origin blog.csdn.net/qq_28245905/article/details/132100686
Recomendado
Clasificación