JavaRush /Blogue Java /Random-PT /Tratamento de exceções em controladores Spring Boot
Павел
Nível 11

Tratamento de exceções em controladores Spring Boot

Publicado no grupo Random-PT
CONTEÚDO DO CICLO DE ARTIGOS Olá de novo! É hora de tirar a poeira do teclado. Crie um projeto de inicialização por mola. Das dependências do maven, precisamos:
<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <java.version>1.8</java.version>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.0.RELEASE</version>
    <relativePath/><!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
Antes de continuar lendo, crie uma estrutura de projeto: Tratamento de exceções em controladores Spring Boot - 1 BusinessException e CustomException:
public class BusinessException extends Exception{
    public BusinessException(String message) {
        super(message);
    }
}

public class CustomException extends Exception{
    public CustomException(String message) {
        super(message);
    }
}
e a classe Response
public class Response {

    private String message;

    public Response() {
    }

    public Response(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
E agora farei um truque com meus ouvidos e passo a palavra para Alexey Kutepov, em seu artigo Exception Handling in Spring Controllers, ele nos dirá como preencher esses arquivos com o conteúdo correto. Leia devagar, copie cuidadosamente todos os exemplos em seu projeto, execute e teste no Postman. Se no artigo do Alexey a seguinte linha levantou dúvidas para você: produz = APPLICATION_JSON_VALUE , então saiba que não tem nada a ver com tratamento de exceções, ela diz que por padrão todos os métodos deste controlador retornarão JSON. Se necessário, em um método específico, esse valor pode ser substituído por outro MediaType. Se você leu, siga em frente. O artigo acima discute diferentes opções de manipuladores. O mais flexível deles: @ControllerAdvice - permite alterar o código e o corpo da resposta padrão em caso de erro. Além disso, permite lidar com várias exceções ao mesmo tempo em um método. Mas isso não é tudo, se você ler mais, obterá o @ControllerAdvice aprimorado totalmente gratuito. Vamos fazer um trabalho preparatório: quero que a resposta exiba mensagens de erro personalizadas e padrão. Para fazer isso, vamos fazer uma alteração na classe Response : adicionar mais um campo
private String debugMessage;
Vamos criar um construtor adicional:
public Response(String message, String debugMessage) {
    this.message = message;
    this.debugMessage = debugMessage;
}
e não se esqueça de criar um Getter e Setter para o novo campo. Agora vamos direto ao ponto. Vamos escrever outro controlador:
@RestController
public class Example7Controller {
    @GetMapping(value = "/testExtendsControllerAdvice")
    public ResponseEntity<?> testExtendsControllerAdvice(@RequestBody Response response) {
        return  ResponseEntity.ok(response);
    }
}
Vamos testar no postman: Envie JSON para http://localhost:8080/testExtendsControllerAdvice
{
    "message": "message"
}
Em resposta receberemos um status de 200 e um corpo
{
    "message": "message",
    "debugMessage": null
}
Agora enviaremos JSON obviamente incorreto
{
    11"message": "message"
}
Em resposta, receberemos o status 400 (se você esqueceu o que significa, procure na Internet) e um corpo de resposta vazio. Claro que ninguém está feliz com isso, vamos lutar contra isso. Anteriormente, criamos @ControllerAdvice do zero, mas no Spring Boot existe um modelo - ResponseEntityExceptionHandler . Já trata muitas exceções, por exemplo: NoHandlerFoundException , HttpMessageNotReadableException , MethodArgumentNotValidException e outras. Esta classe lida com erros. Ele possui vários métodos, cujos nomes são baseados no identificador principal + nome da exceção. Se quisermos tratar alguma exceção básica, então herdamos desta classe e sobrescrevemos o método desejado . Vamos finalizar a classe consultiva padrão
@ControllerAdvice
public class DefaultAdvice extends ResponseEntityExceptionHandler {//унаследовались от обработчика-заготовки

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<Response> handleException(BusinessException e) {
        Response response = new Response(e.getMessage());
        return new ResponseEntity<>(response, HttpStatus.OK);
    }
//Небольшое отступление: В обработчике выше, обратите внимание на HttpStatus.OK,
//он может быть и HttpStatus.BAD_REQUEST or другим, тут ограничений нет,
//попробуйте поменять статусы и потестить этот обработчик


    @Override//переопределor метод родительского класса
    protected ResponseEntity<Object> handleHttpMessageNotReadable
            (HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Response response = new Response("Не правильный JSON",ex.getMessage());
        return new ResponseEntity<>(response, status);
    }
}
Como você notou, o manipulador responsável por HttpMessageNotReadableException foi substituído . Essa exceção ocorre quando o corpo da solicitação que chega ao método do controlador é ilegível - por exemplo, JSON incorreto. O método handleHttpMessageNotReadable () é responsável por esta exceção . Vamos fazer uma requisição novamente com JSON incorreto: para http://localhost:8080/testExtendsControllerAdvice
{
    11"message": "message"
}
Recebemos uma resposta com o código 400 (Bad Request) e o corpo:
{
    "message": "Не правильный JSON",
    "debugMessage": "JSON parse error: Unexpected character ('1' (code 49)): was expecting double-quote to start field name; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('1' (code 49)): was expecting double-quote to start field name\n at [Source: (PushbackInputStream); line: 2, column: 6]"
}
Agora a resposta contém não apenas o código correto, mas também um corpo com mensagens informativas. Vamos verificar como funciona com a solicitação JSON correta:
{
    "message": "message"
}
Recebemos a resposta:
{
    "message": "message",
    "debugMessage": null
}
Para ser sincero, não gosto que a resposta contenha um campo com o valor null , vamos corrigir isso rapidamente agora. Vá para a classe Response e coloque uma anotação no campo obrigatório
@JsonInclude(JsonInclude.Include.NON_NULL)
private String debugMessage;
Reiniciamos o projeto, fazemos novamente a solicitação anterior e na resposta obtemos:
{
    "message": "message"
}
Graças à anotação @JsonInclude(JsonInclude.Include.NON_NULL) , este campo só será incluído na resposta se o especificarmos. @JsonInclude está incluído na biblioteca de anotações de Jackson , é muito útil saber o que ele pode fazer. Aqui estão dois artigos para você escolher: Anotações de Jackson. O autor traduziu, mas não concluiu a tradução ; o Google Translit faz um ótimo trabalho. Validação É necessário complementar este tópico com um conceito como validação. Simplificando, esta é uma verificação de que o objeto é o objeto que esperamos. Por exemplo: se na aplicação “Lista Telefónica” necessitamos verificar a presença de números de telefone na base de dados, então antes de entrar na base de dados é lógico verificar se o utilizador introduziu letras em vez de números. Três artigos sobre validação, em complexidade crescente: Validação de beans no Spring Configurando a validação DTO no Spring Framework Validação de dados no Spring Boot Concluímos a teoria por hoje. Para treinamento, sugiro a seguinte tarefa: Você precisa implementar o aplicativo NightclubBouncer. Requisitos: 1) A aplicação deve aceitar JSON como entrada e gravar no banco de dados. Exemplo JSON:
{
    "name": "Katy Perry"
    “status”:super star”
}
E o corpo da resposta deverá conter a seguinte inscrição: Bem-vindo + nome ! 2) A aplicação deve implementar os seguintes métodos: - enviar um registro por id do banco de dados para o cliente (Postman). - deletar um registro pelo campo: nome . 3) O mapeamento da camada dto para a entidade e vice-versa deve ser implementado . 4) O aplicativo deve gerar um erro KickInTheAssException (você precisa desenvolvê-lo sozinho) se o campo de status no JSON recebido não for igual a: super star 5) O erro KickInTheAssException deve ser tratado por ControllerAdvice e o corpo da resposta deve conter o mensagem: “Não me deixe ver você aqui de novo! O status da resposta deve ser 400. 6) Erro padrão EntityNotFoundException , que ocorre, por exemplo, se apenas Katy Perry veio ao clube e salvou no banco de dados com id = 1 , e você chamou o método “exibir registro por id” e quis para exibir o registro com id = 2 , que não está no banco de dados. Este erro deve ser tratado por um método substituído da classe ResponseEntityExceptionHandler , que cabe a você descobrir sozinho. A resposta deve ter o status apropriado. 7) Faça a validação: uma opção simples - os campos JSON não devem ser nulos, ou mais difícil, o campo "nome" deve ser composto por duas palavras do alfabeto latino e ambas devem começar com letra maiúscula. Valores inválidos devem lançar uma exceção, tratá-la de qualquer forma, imprimir o código de erro apropriado e a mensagem de erro: Não validar. E implemente tudo isso sem usar a biblioteca Lombok, não inclua como dependência do projeto 😅
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION