JavaRush /Blogue Java /Random-PT /Visão geral do REST. Parte 3: Criando um serviço RESTful ...

Visão geral do REST. Parte 3: Criando um serviço RESTful no Spring Boot

Publicado no grupo Random-PT
Esta é a parte final da análise REST. Nas partes anteriores: Visão geral do REST.  Parte 3: Criando um serviço RESTful no Spring Boot - 1

Criando um Projeto

Nesta seção, criaremos um pequeno aplicativo RESTful usando Spring Boot. Nossa aplicação implementará operações CRUD (Criar, Ler, Atualizar, Excluir) nos clientes do exemplo da última parte da análise. Primeiro, vamos criar um novo aplicativo Spring Boot através do menu Arquivo -> Novo -> Projeto... Na janela que se abre, selecione Spring Initializr e especifique Project SDK: Visão geral do REST.  Parte 3: Criando um serviço RESTful no Spring Boot - 2Clique no botão Avançar. Na próxima janela, especifique o tipo de projeto Maven, especifique Grupo e Artefato: Visão geral do REST.  Parte 3: Criando um serviço RESTful no Spring Boot - 3Clique no botão Avançar. Na próxima janela, precisamos selecionar os componentes Spring Framework necessários para o projeto. Spring Web será suficiente para nós: Visão geral do REST.  Parte 3: Criando um serviço RESTful no Spring Boot - 4Clique no botão Avançar. A seguir, resta apenas especificar o nome do projeto e sua localização no sistema de arquivos: Visão geral do REST.  Parte 3: Criando um serviço RESTful no Spring Boot - 5Clique no botão Concluir. O projeto foi criado, agora podemos ver sua estrutura: Visão geral do REST.  Parte 3: Criando um serviço RESTful no Spring Boot - 6IDEA gerou para nós o descritor de implantação do sistema de construção Maven - pom.xml e a classe principal do aplicativo: RestExampleApplication. Aqui está o código deles:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.2.2.RELEASE</version>
       <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.javarush.lectures</groupId>
   <artifactId>rest_example</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>rest_example</name>
   <description>REST example project</description>

   <properties>
       <java.version>1.8</java.version>
   </properties>

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

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
           <exclusions>
               <exclusion>
                   <groupId>org.junit.vintage</groupId>
                   <artifactId>junit-vintage-engine</artifactId>
               </exclusion>
           </exclusions>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
       </plugins>
   </build>

</project>
RestExampleApplication:
@SpringBootApplication
public class RestExampleApplication {

   public static void main(String[] args) {
       SpringApplication.run(RestExampleApplication.class, args);
   }

}

Criando funcionalidade REST

Nosso aplicativo gerencia clientes. Portanto, a primeira coisa que precisamos fazer é criar uma entidade cliente. Esta será uma aula POJO. Vamos criar um pacote modeldentro de um package com.javarush.lectures.rest_example. modelVamos criar uma classe dentro do pacote Client:
public class Client {

   private Integer id;
   private String name;
   private String email;
   private String phone;

   public Integer getId() {
       return id;
   }

   public void setId(Integer id) {
       this.id = id;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getEmail() {
       return email;
   }

   public void setEmail(String email) {
       this.email = email;
   }

   public String getPhone() {
       return phone;
   }

   public void setPhone(String phone) {
       this.phone = phone;
   }
}
O serviço implementará operações CRUD no cliente. A próxima etapa é criar um serviço que implementará essas operações. No pacote com.javarush.lectures.rest_examplecriaremos um pacote service, dentro do qual criaremos uma interface ClientService. Aqui está o código da interface com comentários:
public interface ClientService {

   /**
    * Создает нового клиента
    * @param client - клиент для создания
    */
   void create(Client client);

   /**
    * returns список всех имеющихся клиентов
    * @return список клиентов
    */
   List<client> readAll();

   /**
    * returns клиента по его ID
    * @param id - ID клиента
    * @return - an object клиента с заданным ID
    */
   Client read(int id);

   /**
    * Обновляет клиента с заданным ID,
    * в соответствии с переданным клиентом
    * @param client - клиент в соответсвии с которым нужно обновить данные
    * @param id - id клиента которого нужно обновить
    * @return - true если данные были обновлены, иначе false
    */
   boolean update(Client client, int id);

   /**
    * Удаляет клиента с заданным ID
    * @param id - id клиента, которого нужно удалить
    * @return - true если клиент был удален, иначе false
    */
   boolean delete(int id);
}
Em seguida, precisamos criar uma implementação desta interface. Agora ele atuará como um repositório do cliente Map<Integer, Client>. A chave do cartão será o ID do cliente e o valor será o próprio cliente. Isso foi feito para não sobrecarregar o exemplo com as especificidades do trabalho com o banco de dados. Porém, no futuro poderemos escrever uma implementação diferente da interface na qual será possível conectar um banco de dados real. No pacote servicecriaremos uma implementação da interface ClientService:
@Service
public class ClientServiceImpl implements ClientService {

   // Хранorще клиентов
   private static final Map<Integer, Client> CLIENT_REPOSITORY_MAP = new HashMap<>();

   // Переменная для генерации ID клиента
   private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();

   @Override
   public void create(Client client) {
       final int clientId = CLIENT_ID_HOLDER.incrementAndGet();
       client.setId(clientId);
       CLIENT_REPOSITORY_MAP.put(clientId, client);
   }

   @Override
   public List<Client> readAll() {
       return new ArrayList<>(CLIENT_REPOSITORY_MAP.values());
   }

   @Override
   public Client read(int id) {
       return CLIENT_REPOSITORY_MAP.get(id);
   }

   @Override
   public boolean update(Client client, int id) {
       if (CLIENT_REPOSITORY_MAP.containsKey(id)) {
           client.setId(id);
           CLIENT_REPOSITORY_MAP.put(id, client);
           return true;
       }

       return false;
   }

   @Override
   public boolean delete(int id) {
       return CLIENT_REPOSITORY_MAP.remove(id) != null;
   }
}
A anotação @Serviceinforma ao Spring que esta classe é um serviço. Este é um tipo especial de classe na qual alguma lógica de negócios da aplicação é implementada. Posteriormente, graças a esta anotação, o Spring nos fornecerá uma instância desta classe em locais onde ela for necessária usando Injeção de Dependência. Agora é hora de criar o controlador. Uma classe especial na qual implementamos a lógica para processar solicitações de clientes para endpoints (URIs). Para deixar mais claro, criaremos esta classe em partes. Primeiro, vamos criar a própria classe e introduzir uma dependência nela ClientService:
@RestController
public class ClientController {

   private final ClientService clientService;

   @Autowired
   public ClientController(ClientService clientService) {
       this.clientService = clientService;
   }
}
Vamos esclarecer as anotações: @RestController - informa ao Spring que esta classe é um controlador REST. Aqueles. esta classe implementará a lógica para processar solicitações do cliente @Autowired - informa ao Spring que uma dependência precisa ser injetada neste local. Passamos a interface para o construtor ClientService. Marcamos a implementação deste serviço com uma anotação @Serviceanteriormente, e agora o Spring poderá passar uma instância desta implementação para o construtor do controlador. A seguir, implementaremos passo a passo cada método do controlador para processar operações CRUD. Vamos começar com a operação Create. Para fazer isso, vamos escrever um método create:
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
   clientService.create(client);
   return new ResponseEntity<>(HttpStatus.CREATED);
}
Vejamos este método: @PostMapping(value = "/clients")- aqui queremos dizer que este método processa solicitações POST para o endereço /clients. O método retorna ResponseEntity<?>. ResponseEntity- uma aula especial para retornar respostas. Usando-o, podemos posteriormente retornar o código de status HTTP ao cliente. O método recebe um parâmetro @RequestBody Client client, o valor deste parâmetro é substituído no corpo da solicitação. O resumo fala sobre isso @RequestBody. Dentro do corpo do método, chamamos o método create no serviço criado anteriormente e passamos para ele o controlador cliente aceito nos parâmetros. Em seguida, retornamos o status 201 Criado criando um novo objeto ResponseEntitye passando o valor enum necessário para ele HttpStatus. A seguir implementamos a operação Read: Primeiro implementamos a operação de obtenção de uma lista de todos os clientes disponíveis:
@GetMapping(value = "/clients")
public ResponseEntity<List<Client>> read() {
   final List<Client> clients = clientService.readAll();

   return clients != null &&  !clients.isEmpty()
           ? new ResponseEntity<>(clients, HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Vamos começar a análise: @GetMapping(value = "/clients")- tudo é parecido com a anotação @PostMapping, só que agora processamos solicitações GET. Dessa vez retornamos ResponseEntity<List<Client>>, só que dessa vez, além do status HTTP, retornaremos também o corpo da resposta, que será uma lista de clientes. Nos controladores Spring REST, todos os objetos POJO, bem como coleções de objetos POJO que são retornados como corpos de resposta, são serializados automaticamente para JSON, a menos que seja explicitamente especificado de outra forma. Isso nos convém muito bem. Dentro do método, utilizando nosso serviço, obtemos uma lista de todos os clientes. A seguir, se a lista não for nula ou vazia, retornamos ResponseEntitya lista de clientes e o status HTTP 200 OK usando a classe. Caso contrário, simplesmente retornaremos o status HTTP 404 Not Found. A seguir, implementaremos a capacidade de receber um cliente pelo seu id:
@GetMapping(value = "/clients/{id}")
public ResponseEntity<Client> read(@PathVariable(name = "id") int id) {
   final Client client = clientService.read(id);

   return client != null
           ? new ResponseEntity<>(client, HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Uma novidade é que agora temos uma variável de caminho. Uma variável definida em um URI. value = "/clients/{id}". Nós o indicamos entre chaves. E nos parâmetros do método nós o aceitamos como intuma variável usando a anotação @PathVariable(name = "id"). Este método aceitará solicitações de uri no formato /clients/{id}, onde {id}pode haver qualquer valor numérico. Este valor é posteriormente passado para uma variável int id– o parâmetro do método. No corpo recebemos o objeto Clientutilizando nosso serviço e aceitamos id. E então, por analogia com a lista, retornamos o status 200 OK e o próprio objeto Client, ou simplesmente o status 404 Not Found, se idnão houver nenhum cliente com isso no sistema. Resta implementar duas operações - Atualizar e Excluir. Aqui está o código para esses métodos:
@PutMapping(value = "/clients/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Client client) {
   final boolean updated = clientService.update(client, id);

   return updated
           ? new ResponseEntity<>(HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}

@DeleteMapping(value = "/clients/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
   final boolean deleted = clientService.delete(id);

   return deleted
           ? new ResponseEntity<>(HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
Não há nada essencialmente novo nesses métodos, portanto pularemos uma descrição detalhada. A única coisa que vale a pena mencionar é que o método updateprocessa solicitações PUT (annotation @PutMapping) e o método deleteprocessa solicitações DELETE (annotation DeleteMapping). Aqui está o código completo do controlador:
@RestController
public class ClientController {

   private final ClientService clientService;

   @Autowired
   public ClientController(ClientService clientService) {
       this.clientService = clientService;
   }

   @PostMapping(value = "/clients")
   public ResponseEntity<?> create(@RequestBody Client client) {
       clientService.create(client);
       return new ResponseEntity<>(HttpStatus.CREATED);
   }

   @GetMapping(value = "/clients")
   public ResponseEntity<List<Client>> read() {
       final List<client> clients = clientService.readAll();

       return clients != null &&  !clients.isEmpty()
               ? new ResponseEntity<>(clients, HttpStatus.OK)
               : new ResponseEntity<>(HttpStatus.NOT_FOUND);
   }

   @GetMapping(value = "/clients/{id}")
   public ResponseEntity<Client> read(@PathVariable(name = "id") int id) {
       final Client client = clientService.read(id);

       return client != null
               ? new ResponseEntity<>(client, HttpStatus.OK)
               : new ResponseEntity<>(HttpStatus.NOT_FOUND);
   }

   @PutMapping(value = "/clients/{id}")
   public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Client client) {
       final boolean updated = clientService.update(client, id);

       return updated
               ? new ResponseEntity<>(HttpStatus.OK)
               : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
   }

   @DeleteMapping(value = "/clients/{id}")
   public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
       final boolean deleted = clientService.delete(id);

       return deleted
               ? new ResponseEntity<>(HttpStatus.OK)
               : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
   }
}
Como resultado, a estrutura do nosso projeto fica assim: Visão geral do REST.  Parte 3: Criando um serviço RESTful no Spring Boot - 7

Lançamento e teste

Para executar nossa aplicação, basta executar o método mainna classe RestExampleApplication. E para testar os serviços da web RESTful, você precisa baixar um novo software). O fato é que as solicitações GET são muito fáceis de enviar de um navegador normal, mas para POST, PUT e DELETE um navegador normal não é suficiente. Não se preocupe: você pode usar o Postman para enviar qualquer solicitação HTTP. Você pode baixá-lo aqui . Após baixar e instalar, começamos a testar nosso aplicativo. Para isso, abra o programa e crie uma nova solicitação: Visão geral do REST.  Parte 3: Criando um serviço RESTful no Spring Boot - 9Clique no botão Novo no canto superior esquerdo. Em seguida, selecione Solicitar: Обзор REST. Часть 3: создание RESTful сервиса на Spring Boot - 10Em seguida, dê um nome e salve-o. Vamos agora tentar enviar uma solicitação POST ao servidor e criar o primeiro cliente: Обзор REST. Часть 3: создание RESTful сервиса на Spring Boot - 11Criamos vários clientes desta forma. Em seguida, alteramos o tipo de solicitação para GET e enviamos ao servidor: Обзор REST. Часть 3: создание RESTful сервиса на Spring Boot - 12

Resultados gerais

Parabéns: cobrimos bastante o tópico REST. Todo o material acabou sendo volumoso, mas esperamos que seja útil para você:
  1. Aprendemos o que é REST.

  2. Conhecemos a história do REST.

  3. Conversamos sobre as limitações e princípios deste estilo arquitetônico:

    • trazendo a arquitetura para um modelo cliente-servidor;
    • falta de condição;
    • cache;
    • uniformidade da interface;
    • camadas;
    • código sob demanda (restrição opcional).
  4. Examinamos as vantagens que REST oferece

  5. Examinamos detalhadamente como o servidor e o cliente interagem entre si usando o protocolo HTTP.

  6. Vamos dar uma olhada mais de perto nas solicitações e respostas. Seus componentes foram desmontados.

  7. Por fim, passamos à prática e escrevemos nosso próprio pequeno aplicativo RESTful no Spring Boot. E até aprendemos como testá-lo usando o programa Postman.

Ufa. Acabou sendo volumoso, mas mesmo assim há algo para fazer como lição de casa.

Trabalho de casa

Experimente o seguinte:
  1. Seguindo a descrição acima, crie seu próprio projeto Spring Boot e implemente nele a mesma lógica da palestra. Repita tudo 1 em 1.
  2. Inicie-o. aplicativo.
  3. Baixe e configure o Postman (ou qualquer outra ferramenta para envio de solicitações, até mesmo curl).
  4. Teste as solicitações POST e GET da mesma maneira descrita na aula.
  5. Teste você mesmo as solicitações PUT e DELETE.
Parte 1: O que é REST Parte 2: Comunicação entre cliente e servidor
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION