Um Projeto Spring Boot - P5.3
Tags: tutorial, java, rest, spring
Published: 2023-01-12 20:36:54

Fala pessoal!

Mais um tutorial sobre Rest API em Java com Spring Boot chegando. E hoje vou falar um pouco sobre validações e tratamento de exceções.

---

### Adicionando validação nos campos

Algo que não queremos são erros de exceção gerados no banco de dados por causa de valores que foram enviados de forma incorreta (p.e. null, "" ou email inválido) ou que excedem o tamanho máximo da coluna. 

Então, antes desses dados serem salvos no banco, é preciso validá-los e ai para iniciarmos as validações vamos instalar o Validation:	 

```
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-validation</artifactId>
</dependency> 
```

A segunda coisa a se fazer é adicionar anotações pertinentes aos campos da classe de editora como `@NotBlank` nos itens que não podem ser nulos e nem vazios e `@Size (max = value)` para validar se o item possui tamanho equivalente a column no banco de dados.

---

A [documentação do Bean Validation](https://jakarta.ee/specifications/bean-validation/3.0/apidocs/) possui diversas anotações úteis, como por exemplo de e-mail, vale à pena dar uma conferida. 

---

A classe de Editora ficou assim:
![model de editora](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfewqo6n3ceej5zicsf7.png)

Já o último passo é inserir a anotação `@Valid` nos métodos de POST e PUT lá no controlador:

![methods POST and PUT](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bczf95pubp60s4vkh7nd.png)


E pronto, a validação de Editora está pronta.

Testando a aplicação, quando é enviado um request com todos os dados corretos temos:
![post ok](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ikrb5k66rab2i4fyqma.png)

- retorno do que foi cadastrado + id;
- status = 201

Já quando algum item não foi enviado corretamente, temos um response gigante com várias informações:

![error send request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x253itq3675dhln1caa6.png)

O principal (o que considero) nessa mensagem é:
- status = 400;
- um defaultMessage com o motivo do erro;
- um field com o nome do campo de erro;
- a data e hora que ocorreu o erro.

---

### BÔNUS: Formatando o retorno das exceções

Um bônus para você sobre como formatar o retorno das exceções ao endpoint:


#### P1

- criar uma classe para o tratamento de exceções e extender a classe ResponseEntityExceptionHandler 

+ a minha classe se chama ApiExceptionsHandler e está no pacote library.api.exception

- utilizar a anotação `@ControllerAdvice` que, de forma simples, passa a ideia de que a classe é um componente Spring com o objetivo de tratar exceções globalmente.

- puxar o método handleMethodArgumentNotValid

+ esse método é chamado quando ocorre algum erro que precisa ser retornado ao endpoint. Ele recebe 4 parâmetros:

1. a exceção
2. o cabeçalho
3. o status e 
4. o que foi recebido pela API

- com relação ao retorno do método escolhi o handleExceptionInternal porque ele permite retornar um objeto do tipo Object e ai esse objeto é que será a mensagem de retorno da API.

![class api exceptions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzc1ccuh1n9o1hvw05br.png)


#### P2

- criar uma classe Field dentro do pacote library.api.exception com atributos field (String) e mensage (String)

- adicionar as anotações do Lombok `@AllArgsConstructor` e `@Getter`

![class field](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akt0n5nta9j8xu6bxsh9.png)

- criar a classe Error dentro do pacote library.api.exception
e adicionar os objetos status (int), dateTime (LocalDateTime), title (String), listFields (<Field>)

- adicionar as anotações Lombok `@Getter` e `@Setter`

![class api error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzsdq5jz1y0f6q68edl5.png)

#### P3

- instanciar um array list do tipo Field na classe ApiExceptionHandler. 

É preciso percorrer o objeto ex e para cada erro apontado pela API, pegar qual o campo que está com o erro e qual foi a defaultMessage dele. Cada item da lista de exceptions é um ObjectError e pra conseguir pegar o field é preciso do cast do tipo FieldError. Com isso, uma das possibilidades dese fazer isso é com o seguinte código:

```
for (ObjectError error : ex.getBindingResult().getAllErrors()) {
	String field = ((FieldError) error).getField();
	String message = error.getDefaultMessage();

	fields.add(new Field(field, message));
}
```

- na classe ApiExceptionHandler instanciar um objeto Error e setar os valores

a. status = status.value();
b. dateTime = LocalDateTime.now();
c. title = "Um ou mais campos com preenchimento inválido.";
d. listFields = o array populado anteriormente.

- retornar o erro formatado:
`return handleExceptionInternal(ex, error, headers, status, request);`


![class api exceptions handler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mtrgnya4k6ao0zcap2bd.png)

---

Com isso temos o resultado:

- 1 erro:
![result class api exception handler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5f1hrn2i4wnhom3bf3zs.png)

- mais de 1 erro:

![2 result class api exception handler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvu11oni2oeut46996n9.png)


---

Por hoje é isso! 

O link do repositório no GH: [clica aqui](https://github.com/fllaviacorreia/library-api/tree/develop)

Espero que tenha gostado, curte, compartilha e deixa ttua opinião. 

Até mais!