CodeGym /Blogue Java /Random-PT /Visão geral do REST. Parte 3: Construindo um serviço REST...
John Squirrels
Nível 41
San Francisco

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

Publicado no grupo Random-PT
Esta é a parte final de nossa visão geral do REST. Nas partes anteriores, abordamos: Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 1

Criando um projeto

Nesta seção, criaremos um pequeno aplicativo RESTful usando Spring Boot. Nosso aplicativo implementará operações CRUD (Criar, Ler, Atualizar, Excluir) nos clientes do exemplo da parte anterior da visão geral. Para começar, vamos criar uma nova aplicação Spring Boot através do menu: Arquivo -> Novo -> Projeto... Na janela que se abre, selecione Spring Initializr e especifique o SDK do Projeto: Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 2Clique no botão "Avançar". Na próxima janela, especifique "Projeto Maven" como o tipo de projeto, especifique o "Grupo" e o "Artefato": Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 3Clique no botão "Avançar". Na próxima janela, precisamos selecionar os componentes do Spring Framework necessários para o projeto. Spring Web será suficiente para nós: Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 4Clique no botão "Avançar". Agora só falta indicar o nome do projeto e sua localização no sistema de arquivos: Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 5Clique no botão "Concluir". O projeto está criado e agora podemos ver sua estrutura: Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 6IDEA gerou um descritor de implantação Maven (pom.xml) e a classe principal do aplicativo ( RestExampleApplication) para nós. Aqui está a aparência 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.codegym.lessons/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 é um sistema de gestão de clientes. Portanto, a primeira coisa que precisamos fazer é criar uma entidade cliente. Será uma classe POJO (plain old Java object). Crie um modelpacote dentro do com.codegym.lessons.rest_examplepacote. Dentro do modelpacote, crie o Customer:

public class Customer {

   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 nos clientes. A próxima etapa é criar um serviço que implementará essas operações. No com.codegym.lessons.rest_examplepacote, crie um servicepacote. E dentro disso, crie uma CustomerServiceinterface. Aqui está o código da interface com comentários:

public interface CustomerService {

   /**
    * Creates a new customer
    * @param customer - Customer to be created
    */
   void create(Customer customer);

   /**
    * Returns a list of all existing customers
    * @return List of customers
    */
   List<Customer> readAll();

   /**
    * Returns a customer based on its ID
    * @param id - Customer ID
    * @return - Customer object with the given ID
    */
   Customer read(int id);

   /**
    * Updates the customer with the given ID,
    * according to the passed customer
    * @param customer - Customer to use to update the data
    * @param id - ID of the customer you want to update
    * @return - true if the data has been updated, otherwise false
    */
   boolean update(Customer customer, int id);

   /**
    * Deletes the customer with the given ID
    * @param id - ID of the customer to be deleted
    * @return - true if the customer was deleted, otherwise false
    */
   boolean delete(int id);
}
Em seguida, precisamos implementar essa interface. Agora um Map<Integer, Customer>vai armazenar nossos clientes. As chaves do mapa serão os IDs dos clientes e os valores serão os próprios clientes. Isso é feito para não sobrecarregar este exemplo com as especificidades de trabalhar com um banco de dados real. No entanto, no futuro poderemos escrever outra implementação da interface, que permitirá a conexão com um banco de dados real. No servicepacote, crie uma implementação da CustomerServiceinterface:

@Service
public class CustomerServiceImpl implements CustomerService {

   // Customer repository
   private static final Map<Integer, Customer> CUSTOMER_REPOSITORY_MAP = new HashMap<>();
  
   // Variable for generating a customer ID
   private static final AtomicInteger CUSTOMER_ID_HOLDER = new AtomicInteger();

   @Override
   public void create(Customer customer) {
       final int customerId = CUSTOMER_ID_HOLDER.incrementAndGet();
       customer.setId(customerId);
       CUSTOMER_REPOSITORY_MAP.put(customerId, customer);
   }

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

   @Override
   public Customer read(int id) {
       return CUSTOMER_REPOSITORY_MAP.get(id);
   }

   @Override
   public boolean update(Customer customer, int id) {
       if (CUSTOMER_REPOSITORY_MAP.containsKey(id)) {
           customer.setId(id);
           CUSTOMER_REPOSITORY_MAP.put(id, customer);
           return true;
       }

       return false;
   }

   @Override
   public boolean delete(int id) {
       return CUSTOMER_REPOSITORY_MAP.remove(id) != null;
   }
}
A @Serviceanotação diz ao Spring que esta classe é um serviço. Este é um tipo especial de classe que implementa alguma lógica de aplicativo de negócios. Posteriormente, graças a esta anotação, Spring usará a injeção de dependência para nos fornecer uma instância desta classe em todos os lugares onde ela for necessária. Agora é hora de criar um controlador. Esta é uma classe especial onde iremos implementar a lógica para processamento de requisições de clientes enviadas para endpoints (URIs). Para deixar tudo isso mais claro, vamos criar essa classe de forma incremental. Primeiro, crie a própria classe e adicione uma dependência em CustomerService:

@RestController
public class CustomerController {

   private final CustomerService customerService;

   @Autowired
   public CustomerController(CustomerService customerService) {
       this.customerService = customerService;
   }
}
Vamos explicar as anotações: @RestController informa ao Spring que esta classe é um controlador REST. Em outras palavras, essa classe implementa a lógica para processar as solicitações do cliente. @Autowired informa ao Spring que uma dependência precisa ser adicionada aqui. Passamos a CustomerServiceinterface para o construtor. Anteriormente, marcamos a implementação desse serviço com a @Serviceanotação e agora o Spring poderá passar uma instância dessa implementação para o construtor do controlador. Em seguida, implementaremos cada método do controlador para lidar com operações CRUD. Vamos começar com a operação de criação. Para fazer isso, escrevemos um createmétodo:

@PostMapping(value = "/customers")
public ResponseEntity<?> create(@RequestBody Customer customer) {
   customerService.create(customer);
   return new ResponseEntity<>(HttpStatus.CREATED);
}
Vamos analisar este método: @PostMapping(value = "/customers")significa que este método processa requisições POST enviadas para o endereço "/customers". O método retorna um arquivo ResponseEntity<?>. A ResponseEntityé uma classe especial para retornar respostas. Mais tarde, vamos usá-lo para retornar um código de status HTTP para o cliente. O método tem um @RequestBody Customer customerparâmetro. O valor desse parâmetro vem do corpo da solicitação. A @RequestBodyanotação indica isso. Dentro do corpo do método, chamamos o create()método do serviço criado anteriormente e o passamos para o controller cliente recebido nos parâmetros. Em seguida, retornamos o status "201 Created" criando um novo ResponseEntityobjeto e passando o HttpStatuscampo enum correspondente a ele. Em seguida, vamos implementar oreadoperation: Primeiro, vamos implementar a operação para obter uma lista de todos os clientes disponíveis:

@GetMapping(value = "/customers")
public ResponseEntity<List<Customer>> read() {
   final List<Customer> customers = customerService.readAll();

   return customers != null &&  !customers.isEmpty()
           ? new ResponseEntity<>(customers, HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Vamos nos aprofundar: @GetMapping(value = "/customers")— tudo aqui é semelhante à @PostMappinganotação, mas agora estamos processando solicitações GET. Desta vez retornamos um ResponseEntity<List<Customer>>, e além de um status HTTP, também retornaremos um corpo de resposta, que será a lista de clientes. Nos controladores REST do Spring, tudo são objetos POJO e coleções de objetos POJO, que são retornados como corpos de resposta e serializados automaticamente em JSON, a menos que especificado de outra forma. Isso nos convém perfeitamente. Dentro do método, usamos nosso serviço para obter uma lista de todos os clientes. Em seguida, se a lista não for nula e não estiver vazia, usaremos oResponseEntityclass para retornar a lista de clientes e o código de status HTTP "200 OK". Caso contrário, simplesmente retornamos o código de status HTTP "404 Not Found". Agora vamos implementar a capacidade de obter um cliente usando seu ID:

@GetMapping(value = "/customers/{id}")
public ResponseEntity<Customer> read(@PathVariable(name = "id") int id) {
   final Customer customer = customerService.read(id);

   return customer != null
           ? new ResponseEntity<>(customer, HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Uma coisa nova aqui é a variável de caminho. A variável é definida no URI: value = "/customers/{id}". Nós o indicamos entre chaves. E nós o recebemos como um intparâmetro de método usando a @PathVariable(name = "id")anotação. Este método aceitará requisições enviadas para URIs na forma /customers/{id}, onde {id}representa qualquer valor numérico. Este valor é posteriormente passado através da int idvariável para o parâmetro do método. No corpo, obtemos o Customerobjeto usando nosso serviço e o recebido id. E então, por analogia com a lista, retornamos o status "200 OK" e o Customerpróprio objeto, ou simplesmente o status "404 Not Found" se o sistema não tiver nenhum cliente com isso id. Ainda precisamos implementar duas operações: atualizar e excluir. Aqui está o código para esses métodos:

@PutMapping(value = "/customers/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Customer customer) {
   final boolean updated = customerService.update(customer, id);

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

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

   return deleted
           ? new ResponseEntity<>(HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
Não há nada essencialmente novo nesses métodos, então vamos pular a descrição detalhada. A única coisa que vale a pena mencionar é que o update()método lida com solicitações PUT ( @PutMappinganotação) e o delete()método lida com solicitações DELETE ( DeleteMappinganotação). Aqui está o código completo para o controlador:

@RestController
public class CustomerController {

   private final CustomerService customerService;

   @Autowired
   public CustomerController(CustomerService customerService) {
       this.customerService = customerService;
   }

   @PostMapping(value = "/customers")
   public ResponseEntity<?> create(@RequestBody Customer customer) {
       customerService.create(customer);
       return new ResponseEntity<>(HttpStatus.CREATED);
   }

   @GetMapping(value = "/customers")
   public ResponseEntity<List<Customer>> read() {
       final List<Customer> customers = customerService.readAll();

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

   @GetMapping(value = "/customers/{id}")
   public ResponseEntity<Customer> read(@PathVariable(name = "id") int id) {
       final Customer customer = customerService.read(id);

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

   @PutMapping(value = "/customers/{id}")
   public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Customer customer) {
       final boolean updated = customerService.update(customer, id);

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

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

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

Lançamento e teste

Para iniciar nossa aplicação, basta executar o main()método na RestExampleApplicationclasse. Mas, para testar os serviços da Web RESTful, precisamos fazer download de software adicional. O fato é que as solicitações GET são bastante simples de enviar de um navegador comum, mas um navegador comum não pode enviar solicitações POST, PUT e DELETE. Não se preocupe: você pode usar um programa chamado Postman para enviar qualquer solicitação HTTP. Você pode baixá-lo aqui . Depois de baixar e instalar o Postman, começamos a testar nosso aplicativo. Para fazer isso, abra o programa e crie uma nova solicitação: Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 9Clique no botão "Novo" no canto superior esquerdo. Em seguida, selecione "Solicitar": Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 10Em seguida, dê um nome e salve-o. Agora vamos tentar enviar uma requisição POST para o servidor e criar o primeiro cliente: Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 11Criamos vários clientes desta forma. Em seguida, alteramos o tipo de solicitação para GET e enviamos a solicitação ao servidor: Visão geral do REST.  Parte 3: Construindo um serviço RESTful no Spring Boot - 12

Resumo

Parabéns! Cobrimos REST o suficiente. Houve um grande volume de material, mas espero que tenha sido útil para você:
  1. Aprendemos o que é REST.

  2. Aprendemos como o REST surgiu.

  3. Conversamos sobre as limitações e princípios por trás desse estilo arquitetônico:

    • arquitetura cliente-servidor
    • apátrida
    • cache
    • interface uniforme
    • camadas
    • código sob demanda (opcional)
  4. Exploramos os benefícios fornecidos pelo REST

  5. Examinamos em detalhes como o servidor e o cliente interagem entre si por meio do protocolo HTTP.

  6. Examinamos mais de perto as solicitações e as respostas. Dissecamos suas partes constituintes.

  7. Por fim, obtivemos alguma experiência prática escrevendo nosso próprio pequeno aplicativo RESTful usando Spring Boot. E até aprendemos a testá-lo usando o Postman.

Ufa. Isso foi muito, mas ainda há algo para você fazer como lição de casa.

Trabalho de casa

Tente o seguinte:
  1. Seguindo a descrição acima, crie seu próprio projeto Spring Boot e implemente a mesma lógica da lição. Repita tudo exatamente.
  2. Inicie o aplicativo.
  3. Baixe e configure o Postman (ou qualquer outra ferramenta para enviar solicitações, por exemplo, curl).
  4. Teste as solicitações POST e GET da mesma maneira descrita na lição.
  5. Teste as solicitações PUT e DELETE você mesmo.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION