Esta é a parte final da análise REST. Nas partes anteriores:
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: Clique no botão Avançar. Na próxima janela, especifique o tipo de projeto Maven, especifique Grupo e Artefato: Clique 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: Clique no botão Avançar. A seguir, resta apenas especificar o nome do projeto e sua localização no sistema de arquivos: Clique no botão Concluir. O projeto foi criado, agora podemos ver sua estrutura: IDEA 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 pacotemodel
dentro de um package com.javarush.lectures.rest_example
. model
Vamos 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_example
criaremos 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 service
criaremos 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 @Service
informa 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 @Service
anteriormente, 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 ResponseEntity
e 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 ResponseEntity
a 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 int
uma 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 Client
utilizando 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 id
nã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 update
processa solicitações PUT (annotation @PutMapping
) e o método delete
processa 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:
Lançamento e teste
Para executar nossa aplicação, basta executar o métodomain
na 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: Clique no botão Novo no canto superior esquerdo. Em seguida, selecione Solicitar: Em seguida, dê um nome e salve-o. Vamos agora tentar enviar uma solicitação POST ao servidor e criar o primeiro cliente: Criamos vários clientes desta forma. Em seguida, alteramos o tipo de solicitação para GET e enviamos ao servidor:
Resultados gerais
Parabéns: cobrimos bastante o tópico REST. Todo o material acabou sendo volumoso, mas esperamos que seja útil para você:-
Aprendemos o que é REST.
-
Conhecemos a história do REST.
-
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).
-
Examinamos as vantagens que REST oferece
-
Examinamos detalhadamente como o servidor e o cliente interagem entre si usando o protocolo HTTP.
-
Vamos dar uma olhada mais de perto nas solicitações e respostas. Seus componentes foram desmontados.
-
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.
Trabalho de casa
Experimente o seguinte:- 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.
- Inicie-o. aplicativo.
- Baixe e configure o Postman (ou qualquer outra ferramenta para envio de solicitações, até mesmo curl).
- Teste as solicitações POST e GET da mesma maneira descrita na aula.
- Teste você mesmo as solicitações PUT e DELETE.
GO TO FULL VERSION