Spring

Spring Web

Introdução

Implementar recursos HTTP para interação de arquivos JSON pelas aplicações.

Antes de tudo, precisamos esclarecer que NÃO se trata de Spring Web MVC

O que é API

Uma API (interface application program) é um código programável que faz a “ponte” de comunicação entre duas aplicações distintas.

REST e RESTful

A API REST (representational state transfer) é como um guia de boas práticas e RESTful é a capacidade de determinado sistema aplicar os princípios de REST.

Princípios

Para que uma arquitetura seja RESTful, é necessário ter uma série de princípios ou padrões. Vejamos quais são eles:

  • cliente-servidor — significa aprimorar a portabilidade entre várias plataformas de interface do usuário e do servidor, permitindo uma evolução independente do sistema;
  • interface uniforme — representa uma interação uniforme entre cliente e servidor. Para isso, é preciso ter uma interface que identifique e represente recursos, mensagens autodescritivas, bem como hypermedia (HATEOAS);
  • stateless — indica que cada interação via API tem acesso a dados completos e compreensíveis;
  • cache — necessário para reduzir o tempo médio de resposta, melhorar a eficiência, desempenho e escalabilidade da comunicação;
  • camadas — permite que a arquitetura seja menos complexa e altamente flexível.

Nível de Maturidade

Para padronizar e facilitar o desenvolvimento de APIs REST, Leonard Richardson propôs um modelo de maturidade para esse tipo de API, definido em 4 níveis.

Nível 0: Ausência de Regras

Esse é considerado o nível mais básico de uma API, quem implementa apenas esse nível não pode ser considerada REST pois não segue qualquer padrão.

Um único verbo com nomes que não seguem nenhum padrão

Nível 1: Aplicação de Resources

Observe que o nome dos recursos foram equalizados e para não gerar ambiguidade é necessário definir o verbo apropriadamente.

Nível 2: Implementação de verbos HTTP

Como a definição dos verbos já foi requisitada no Nível 1, o Nível 2 se encarrega de validar a aplicabilidade dos verbos para finalidades específicas como:

Existe uma discussão quando precisamos retornar dados, através de parâmetros via body, recebidos pelo método POST.

Nível 3: HATEOAS

HATEOAS significa Hypermedia as the Engine of Application State. Uma API que implementa esse nível fornece aos seus clientes links que indicarão como poderá ser feita a navegação entre seus recursos. Ou seja, quem for consumir a API precisará saber apenas a rota principal e a resposta dessa requisição terá todas as demais rotas possíveis.

O nível 3 é sem dúvidas o menos explorado, muitas APIs existentes no mercado não implementam esse nível.

No exemplo acima, podemos ver o resultado de uma API que implementa HATEAOS, veja que na resposta dessa API há uma coleção “links”, cada link aponta para uma rota dessa API. No caso desse exemplo, temos um link para a própria rota, um link para alterar um cliente e outra para exlcuir.

Springboot REST API

Se preferir criar um projeto Spring Boot do zero, acesse o Primeiro Projeto.

Ou simplesmente adicione o starter do spring-stater-web.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
Com esta dependência o Spring Boot oferece um container TomCat embutido

Criando nosso primeiro Controller

Um controller é um recurso que disponibiliza as funcionalidades de negócio da aplicação, através do protocolo HTTP. Vamos criar uma classe de exemplo:

Crie a classe WelcomeController.java

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;

@RestController
public class WelcomeController {
    @GetMapping("/welcome")
    public String welcome(){
        return "Welcome to a Spring Boot REST API";
    }
}

Inicialize a aplicação através da sua classe que contenha o método main e a anotação @SpringBootApplication.

Observe que o log da aplicação agora sinaliza que um servidor TomCat foi iniciado na porta 8080.
o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
o.apache.catalina.core.StandardService   : Starting service [Tomcat]
org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.52]
o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2240 ms
o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
d.a.AccessControlApplication             : Started AccessControlApplication in 3.659

No navegador digite: http://localhost:8080/welcome

Primeiro Controller

Vamos criar nosso primeiro controller, para uma demonstração de uso de nossos serviços pela web.

Rest Controller

Um Rest Controller em Spring, nada mais é que uma classe contendo anotações específicas para a disponibilização de recursos HTTPs, baseados em nossos serviços e regras de negócio.

Anotações e configurações mais comuns:

  • @RestController: Responsável por designar o bean de compoment, que surporta requisições HTTP com base na arquitetura REST.
  • @RequestMapping("prefix"): Determina qual a URI comum para todos os recursos disponibilizados pelo Controller.
  • @GetMapping: Determina que o método aceitará requisições HTTP do tipo GET.
  • @PostMapping: Determina que o método aceitará requisições HTTP do tipo POST.
  • @PutMapping: Determina que o método aceitará requisições HTTP do tipo PUT.
  • @DeleteMapping: Determina que o método aceitará requisições HTTP do tipo DELETE.
  • @RequestBody: Converte um JSON para o tipo do objeto esperado como parâmetro no método.
  • @PathVariable: Consegue determinar que parte da URI será composta por parâmetros recebidos nas requisições.

Controle de Usuários

Vamos disponibilizar as funcionalidades de CRUD, da entidade User.java através de uma API:

Antes de iniciar esta etapa, recomendamos revisar a etapa abaixo ou criar um Repositorio Fake.
public class Usuario {
    private Integer id;
    private String login;
    private String password;
    public Usuario() {}
    public Usuario(String login, String password) {
        this.login = login;
        this.password = password;
    }
    @Override
    public String toString() {
        return "User{" +
                "login='" + login + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
    
    ... getters e setters
}
🔔 Atenção
A classe UserRepository pode ser substituída pela implementação no tópico Spring Data Jpa.
@Repository
public class UserRepository {
    public void save(Usuario usuario){
        if(usuario.getId()==null)
          System.out.println("SAVE - Recebendo o usuário na camada de repositório");
        else
         System.out.println("UPDATE - Recebendo o usuário na camada de repositório");
        
        System.out.println(usuario);
    }
    public void deleteById(Integer id){
        System.out.println(String.format("DELETE/id - Recebendo o id: %d para excluir um usuário", id));
        System.out.println(id);
    }
    public List<Usuario> findAll(){
        System.out.println("LIST - Listando os usários do sistema");
        List<Usuario> usuarios = new ArrayList<>();
        usuarios.add(new Usuario("gleyson","password"));
        usuarios.add(new Usuario("frank","masterpass"));
        return usuarios;
    }
    public Usuario findById(Integer id){
        System.out.println(String.format("FIND/id - Recebendo o id: %d para localizar um usuário", id));
        return new Usuario("gleyson","password");
    }
     public Usuario findByUsername(String username){
        System.out.println(String.format("FIND/username - Recebendo o usernamae: %s para localizar um usuário", username));
        return new Usuario("gleyson","password");
    }
}

Confirme a existência do Starter Web no pom.xml:

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

Primeiro criamos a classe controller UserController.java inserindo conforme código abaixo:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserRepository repository;
    @GetMapping
    public List<User> list(){
        return repository.findAll();
    }
    @PostMapping
    public void save(@RequestBody User user){
        repository.save(user);
    }
    @PutMapping
    public void update(@RequestBody User user){
        repository.save(user);
    }
    @GetMapping("/{username}")
    public User find(@PathVariable("/username") String username){
        return repository.findByUsername(username);
    }
    @DeleteMapping("/{id}")
    public void delete(@PathVariable("/id") Integer id){
        repository.deleteById(id);
    }
}

Detalhando nosso código:

  • Na linha 7: Definimos que nosso component é um REST Controller;
  • Na linha 8: Podemos definir um sufixo para todos os nossos recursos HTTPs;
  • Na linha 10: Realizamos a injeção de dependência do nosso UserRepository;
  • Na linha 12: Temos um método GET/users para retornar todos os usuários;
  • Na linha 16: Temos um método POST/users para salvar um novo usuário;
  • Na linha 20: Temos um método PUT/users para alterar um usuário;
  • Na linha 24: Temos um método GET/users/{username} para retornar um usuário pelo seu username;
  • Na linha 28: Temos um método DELETE/users/{id} para remover um usuário pelo seu id.

Testando os serviços

O navegador consegue testar nossa aplicação somente com os métodos GET, mas como precisaremos realizar outros tipos de operações, será necessário ter um programa que interaja melhor com a API.

https://www.postman.com/downloads/

Após iniciar a aplicação Spring Boot, vamos incluir um usuário conforme requisição abaixo:

POST: http://localhost:8080/users

{"name":"GLEYSON SAMPAIO", "username":"glysns","password":"digytal@2021"}

Listando nossos usuários:

Alterando um usuário:

Esta etapa é muito semelhante ao método Save POST, a diferença é que incluímos o atributo id para identificar ao Spring Data JPA que ele deverá alterar o registro existente.

{
    "id": 1,
    "name": "GLEYSON SAMPAIO DE OLIVEIRA",
    "username": "glysns",
    "password": "digytal@2021"
}