API REST com Java e Spring Boot

API REST com Java e Spring Boot (Parte 2)


Um passo a passo para você aprender a fazer uma API REST com Java e Spring Boot do começo ao fim — Parte 2: Classes, camadas e endpoint


Esta é a parte 2 da série API REST com Java e Spring Boot. Se você chegou direto aqui, comece pela parte 1.

Aqui trataremos da criação da API em si, com base na utilização das ferramentas mencionadas anteriormente.

API REST com Java e Spring Boot

Logo após a importação do projeto criado por meio do Spring Initializr, vemos que foi automaticamente criada uma classe dentro do pacote principal do projeto. Essa classe é formada pela composição do nome do projeto (no nosso caso, “RestApi”) com a palavra “Application”.

Classe RestApiApplication
Classe RestApiApplication

Essa simples classe possui um método main, o qual chama o método run da classe SpringApplication, fazendo assim com que a aplicação suba quando o método é executado. Como dito anteriormente, o servidor Tomcat já está embutido e é inicializado (por padrão na porta 8080) à execução do método.

Uma vez que a aplicação esteja no ar e funcionando corretamente, podemos prosseguir para a criação do endpoint para o cadastro de usuários.

Porém, antes de começarmos a criar nossas classes, vamos colocar uma nova dependência em nosso projeto: a biblioteca do Project Lombok. Basta colar o código da dependency no arquivo pom.xml e recarregar o Maven. Feito isso, podemos usar as anotações do Lombok para nos poupar tempo de digitação. Falaremos sobre essas anotações logo mais.

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.18.22</version>
   <scope>provided</scope>
</dependency>

Uma boa forma de organizarmos o projeto é separarmos as classes dentro de pacotes que representam camadas que se interconectam. No nosso caso, utilizaremos uma arquitetura que separa nossas classes em ServiceModel, Controller Repository.

Em nossa camada model, teremos uma classe User. Ela conterá os dados do usuário (nome, e-mail, CPF e data de nascimento).

Classe User
Classe User

Por se tratar de uma entidade da JPA, isto é, uma classe que representará as tabelas no banco de dados, devemos acrescentar a anotação @Entity em cima da classe.

Além disso, devemos utilizar também as anotações @Id e @GeneratedValue(strategy = GenerationType.IDENTITY) no atributo id, pois se trata da chave primária — gerada automaticamente pelo banco de dados.

Por fim, as anotações @Column e @Size servem apenas para especificar algumas características das colunas da tabela.

Desse modo, a tabela é criada automaticamente no banco de dados com suas respectivas colunas.

Os métodos getters e setters de todos seus atributos e também os métodos equals e hashcode são gerados automaticamente pelo Lombok, basta usar a anotação @Data na classe. Desse modo, o código fica muito mais limpo e sua escrita fica muito mais ágil.

O objetivo do endpoint é cadastrar usuários, isto é, devemos utilizar — no contexto REST e HTTP — o método POST para registrar os dados no banco. Para fins de consulta devemos também utilizar o método GET. Já para atualizar e para deletar algum registro, utilizamos os métodos PUT e DELETE, respectivamente. Por ora, trabalharemos apenas com GET e POST. É isto que veremos a seguir.

Trabalhando com o endpoint de Usuários

A fim de que o User se conecte com o servidor, precisamos criar um controller para o usuário. Para isso, criamos a classe UserController no pacote com.example.rest.controller.

Controller

controller serve para mapear o endereço que, quando chamado, devolverá os usuários cadastrados (GET) ou possibilitará sua inserção, atualização ou remoção do sistema (POST, PUT, DELETE).

Classe UserController
Classe UserController

Analisando a classe UserController, percebemos que há nela diversas anotações. Essas anotações fazem parte do Spring Web (dependência que adicionamos na criação do projeto).

Em cima de UserController podemos ver as anotações @RestController e @RequestMapping(“/users”).

A primeira serve para indicar ao Spring que a classe se trata de um controller que deve ser gerenciado de acordo com o REST. Isso significa que já fica subentendido, por padrão, o uso do @ResponseBody, ou seja, o Spring devolve o próprio retorno do método (em JSON), sem esperar a navegação para uma página (JSP, por exemplo).

A segunda anotação apenas indica para o Spring o mapeamento da classe.

Service

Podemos observar também o atributo userService anotado com @Autowired. Nesta arquitetura que estamos usando, o service é a camada na qual ficará contida toda a nossa lógica.

Nosso service terá a lógica dos métodos CRUD e também alguns métodos para realizar a conversões de e para objetos do tipo DTO (explicarei a seguir).

Classe UserService
Classe UserService

Aqui utilizamos a anotação @Service para indicar ao Spring que essa classe se trata de um serviço. Também precisamos injetar, por meio da anotação @Autowired, um userRepository.

Repository

repository é uma interface que herda diversos métodos já prontos e abstraídos de outra interface. Isso nos poupa o trabalho de ficar criando métodos para as operações CRUD que lidam com o banco de dados em nosso projeto.

Para isso, devemos criar no pacote com.example.rest.repository a interface UserRepository, que herda a interface JpaRepository, passando como parâmetros do generics a entidade com a qual essa interface trabalha e o tipo do atributo da chave primária dessa entidade. Além disso, utilizamos a anotação @Repository para indicar ao Spring que a interface em questão se trata de um repositório.

E é só isso. Ao declaramos um userRepository em nosso Service, praticamente todos os métodos de que necessitamos já estarão prontos para serem usados.

Interface UserRepository
Interface UserRepository

Agora vamos voltar ao Controller e analisar os métodos GET e POST.

Método GET

Os métodos list e findById têm a anotação @GetMapping, a qual indica que estes são os métodos que serão chamados quando utilizarmos o GET no endereços mapeados para “/user” e “user/{id}”, respectivamente. Podemos observar também que os métodos, utilizando o service, retornam objetos do tipo UserDto. A anotação @PathVariable no método de busca por id serve para indicar ao Spring que seu parâmetro é o mesmo que foi passado em @GetMapping.

Métodos GET na classe UserController
Métodos GET na classe UserController

DTO

Mas o que seria esse UserDto? Ora, é sabido que não é uma boa prática devolvermos entidades da JPA (classes vinculadas ao banco de dados) no controller. Como nossa classe User é uma dessas entidades, ela pode conter dados sensíveis e privados que devem se manter afastados da conexão direta com o controller. Além disso, se devolvêssemos um User em vez de um UserDto, todos os atributos da classe User seriam serializados e devolvidos no JSON, sem que pudéssemos escolher os atributos desejados — ou seja, perderíamos flexibilidade.

Por isso, devemos criar um DTO (Data Transfer Object), uma classe que representa somente os dados que queremos devolver neste endpoint. No nosso caso, criaremos a classe dentro do pacote com.example.rest.model.dto.

Classe UserDto
Classe UserDto

A classe terá os atributos mostrados acima, um construtor default e um construtor que recebe como parâmetro um objeto do tipo User, o qual já contém todas as informações requeridas (id, nome, e-mail, CPF e data de nascimento). Como estamos utilizando o Lombok, também podemos anotar a classe com @Data, poupando-nos assim de criar todos os getters e setters.

A anotação @JsonFormat acima da data serve para especificarmos o padrão textual pelo qual receberemos o JSON na requisição.

Dessa forma, para retornarmos devidamente objetos UserDto nos métodos do Controller, precisamos utilizar o userService que foi injetado, já que na classe UserService também injetamos o userRepository, o qual já traz consigo seus métodos prontos. No nosso caso, os métodos adequados são o findAll, para a lista de todos os usuários, e o findById, para um usuário específico. Após sua utilização só precisamos fazer a conversão com os métodos convertListToDto e convertToDto.

Métodos de conversão na classe UserService
Métodos de conversão na classe UserService

Método POST

Outro método contido na classe UserController é o register, que recebe a anotação @PostMapping e retorna um UserDto.

Analogamente à @GetMapping, a @PostMapping indica ao Spring que o método que será chamado quando utilizarmos o POST em uma requisição mapeada para “/user” será o register.

Método POST na classe UserController
Método POST na classe UserController

Vemos que o método register recebe um parâmetro do tipo UserForm. A classe UserForm trata-se também de um DTO. Contudo, como estamos lidando com um POST, os dados vão do cliente para a API (e não o contrário), então utilizamos o padrão de nomenclatura Form. Temos então a classe UserForm dentro do pacote com.example.rest.model.form.

Classe UserForm
Classe UserForm

Form

Da mesma forma que a classe UserDto, a classe UserForm contém atributos de User e a anotação @Data do Lombok. Além disso, podemos ver anotações sobre os atributos. A @JsonFormat, tal como vimos em UserDto, serve para especificarmos o padrão de passagem de parâmetros no JSON. As outras anotações fazem parte do Bean Validation, que adicionamos como dependência na criação do projeto, por meio da Validation.

Bean Validation

Bean Validation é uma especificação do Java que se integra com o Spring. Ela permite que a validação seja feita por meio de anotações, o que nos poupa muito tempo e trabalho. Assim, não precisamos escrever código para cada validação, com “ifs e elses” espalhados por nossas classes.

Então, se quisermos confirmar que o atributo enviado via POST não é nulo, é só utilizarmos @NotNull. Para atributos vazios, utilizamos @NotBlank. E ainda temos anotações mais específicas, como a @CPF e a @Email, que verificam a validade dos dados passados. Existem muitas outras anotações que podem ser aproveitadas.

De volta ao Controller

Seguindo no método register do UserController, vemos que o parâmetro userForm tem as anotações @RequestBody e @Valid na frente. Esta última é necessária para o funcionamento das validações do Bean Validation. A primeira é necessária para avisar ao Spring que, por se tratar de um POST, os parâmetros devem ser obtidos no corpo da requisição (e não na URL, como no método GET).

Assim, ao fazermos uma requisição POST, conseguimos criar um objeto User, o qual nós podemos, então, gravar no banco de dados por meio do método createUser (do Service), que utiliza o método save (do Repository). Igualmente ao que acontece com as requisições GET, temos uma transição entre camadas até chegarmos ao Repository e seus métodos prontos.

Outra coisa a se observar no método register é a anotação @ResponseStatus. Ela serve para especificar o tipo de retorno que obteremos ao fazer uma requisição via POST. Se não definirmos nada, o retorno que teremos será o código 200, que significa apenas “OK”. Se a requisição funcionar corretamente, com o uso dessa anotação e com o parâmetro que foi passado obteremos então o código 201 (“Created”), que significa que algo foi criado com sucesso.

Com isso, já temos as classes criadas e o endpoint praticamente pronto para ser testado. Mas, antes disso, na Parte 3 veremos como ficará o nosso banco de dados ao rodarmos essa aplicação.

Você pode ver o código completo no GitHub.


Está entrando no mundo da programação? Uma boa dica para iniciar seus estudos são os livros Lógica de Programação – JavaScript e HTML e Entendendo Algoritmos – guia ilustrado.

Está interessado em consumir uma API externa? Veja o artigo Consumindo API com Java, Spring Boot e Gradle.

Quer saber mais sobre APIs REST? Leia nosso artigo sobre as 9 melhores práticas!


Posts relacionados

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *