Esta es la parte final del análisis REST. En partes anteriores:
Creando un proyecto
En esta sección, crearemos una pequeña aplicación RESTful usando Spring Boot. Nuestra aplicación implementará operaciones CRUD (Crear, Leer, Actualizar, Eliminar) en los clientes del ejemplo de la última parte del análisis. Primero, creemos una nueva aplicación Spring Boot a través del menú Archivo -> Nuevo -> Proyecto... En la ventana que se abre, seleccione Spring Initializr y especifique Project SDK: haga clic en el botón Siguiente. En la siguiente ventana, especifique el tipo de proyecto Maven, especifique Grupo y Artefacto: haga clic en el botón Siguiente. En la siguiente ventana, debemos seleccionar los componentes de Spring Framework necesarios para el proyecto. Spring Web será suficiente para nosotros: haga clic en el botón Siguiente. A continuación, sólo queda especificar el nombre del proyecto y su ubicación en el sistema de archivos: Haga clic en el botón Finalizar. El proyecto ha sido creado, ahora podemos ver su estructura: IDEA ha generado para nosotros el descriptor de implementación del sistema de compilación Maven - pom.xml y la clase de aplicación principal:RestExampleApplication
. Aquí está su código:
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>
RestExampleAplicación:
@SpringBootApplication
public class RestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(RestExampleApplication.class, args);
}
}
Creando funcionalidad REST
Nuestra aplicación gestiona clientes. Entonces, lo primero que debemos hacer es crear una entidad de cliente. Esta será una clase POJO. Creemos un paquetemodel
dentro de un paquete com.javarush.lectures.rest_example
. model
Creemos una clase dentro del paquete 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;
}
}
El servicio implementará operaciones CRUD en el cliente. El siguiente paso es crear un servicio que implementará estas operaciones. En el paquete com.javarush.lectures.rest_example
crearemos un paquete service
, dentro del cual crearemos una interfaz ClientService
. Aquí está el código de la interfaz con comentarios:
public interface ClientService {
/**
* Создает нового клиента
* @param client - клиент для создания
*/
void create(Client client);
/**
* devoluciones список всех имеющихся клиентов
* @return список клиентов
*/
List<client> readAll();
/**
* devoluciones клиента по его ID
* @param id - ID клиента
* @return - un objeto клиента с заданным 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);
}
A continuación necesitamos crear una implementación de esta interfaz. Ahora actuará como un repositorio de cliente Map<Integer, Client>
. La clave de la tarjeta será la identificación del cliente y el valor será el propio cliente. Esto se hizo para no sobrecargar el ejemplo con los detalles específicos de trabajar con la base de datos. Sin embargo, en el futuro podremos escribir otra implementación de la interfaz en la que será posible conectar una base de datos real. En el paquete service
crearemos una implementación de la interfaz ClientService
:
@Service
public class ClientServiceImpl implements ClientService {
// Хранoще клиентов
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;
}
}
La anotación @Service
le dice a Spring que esta clase es un servicio. Este es un tipo especial de clase en el que se implementa cierta lógica empresarial de la aplicación. Posteriormente, gracias a esta anotación, Spring nos proporcionará una instancia de esta clase en los lugares donde sea necesaria mediante Dependency injection. Ahora es el momento de crear el controlador. Una clase especial en la que implementamos la lógica para procesar solicitudes de clientes para puntos finales (URI). Para que quede más claro, crearemos esta clase en partes. Primero, creemos la clase en sí e introduzcamos una dependencia de ella ClientService
:
@RestController
public class ClientController {
private final ClientService clientService;
@Autowired
public ClientController(ClientService clientService) {
this.clientService = clientService;
}
}
Aclaremos las anotaciones: @RestController : le dice a Spring que esta clase es un controlador REST. Aquellos. esta clase implementará la lógica para procesar las solicitudes de los clientes @Autowired : le dice a Spring que se debe inyectar una dependencia en este lugar. Pasamos la interfaz al constructor ClientService
. Marcamos la implementación de este servicio con una anotación @Service
anteriormente, y ahora Spring podrá pasar una instancia de esta implementación al constructor del controlador. A continuación, implementaremos paso a paso cada método de controlador para procesar operaciones CRUD. Comencemos con la operación Crear. Para hacer esto, escribamos un método create
:
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
clientService.create(client);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Veamos este método: @PostMapping(value = "/clients")
- aquí queremos decir que este método procesa solicitudes POST a la dirección /clients. El método devuelve ResponseEntity<?>
. ResponseEntity
- una clase especial para devolver respuestas. Al usarlo, luego podemos devolver el código de estado HTTP al cliente. El método toma un parámetro @RequestBody Client client
, el valor de este parámetro se sustituye en el cuerpo de la solicitud. El resumen habla de esto @RequestBody
. Dentro del cuerpo del método, llamamos al método create en el servicio creado previamente y le pasamos el controlador del cliente aceptado en los parámetros. Luego devolvemos el estado 201 Creado creando un nuevo objeto ResponseEntity
y pasándole el valor de enumeración requerido HttpStatus
. A continuación, implementamos la operación Read
: Primero, implementamos la operación de obtener una lista de todos los clientes disponibles:
@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);
}
Comencemos el análisis: @GetMapping(value = "/clients")
- todo es similar a la anotación @PostMapping
, solo que ahora procesamos solicitudes GET. Esta vez regresamos ResponseEntity<List<Client>>
, solo que esta vez, además del estado HTTP, también devolveremos el cuerpo de la respuesta, que será una lista de clientes. En los controladores Spring REST, todos los objetos POJO, así como las colecciones de objetos POJO que se devuelven como cuerpos de respuesta, se serializan automáticamente en JSON a menos que se especifique explícitamente lo contrario. Esto nos viene bastante bien. Dentro del método, utilizando nuestro servicio, obtenemos una lista de todos los clientes. A continuación, si la lista no es nula o está vacía, devolvemos ResponseEntity
la lista de clientes y el estado HTTP 200 OK usando la clase. De lo contrario, simplemente devolvemos el estado HTTP 404 No encontrado. A continuación, implementaremos la capacidad de recibir un cliente por su identificación:
@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);
}
Algo nuevo es que ahora tenemos una variable de ruta. Una variable que se define en un URI. value = "/clients/{id}"
. Lo indicamos entre llaves. Y en los parámetros del método lo aceptamos como int
variable usando la anotación @PathVariable(name = "id")
. Este método aceptará solicitudes de uri del formulario /clients/{id}
, donde en su lugar {id}
puede haber cualquier valor numérico. Este valor se pasa posteriormente a una variable int id
: el parámetro del método. En el cuerpo recibimos el objeto Client
usando nuestro servicio y lo aceptamos id
. Y luego, por analogía con la lista, devolvemos el estado 200 OK y el objeto en sí Client
, o simplemente el estado 404 No encontrado, si id
no hay ningún cliente con esto en el sistema. Queda por implementar dos operaciones: Actualizar y Eliminar. Aquí está el código para estos 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);
}
No hay nada esencialmente nuevo en estos métodos, por lo que nos saltaremos una descripción detallada. Lo único que vale la pena mencionar es que el método update
procesa solicitudes PUT (anotación @PutMapping
) y el método delete
procesa solicitudes DELETE (anotación DeleteMapping
). Aquí está el código completo del 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, la estructura de nuestro proyecto se ve así:
Lanzamiento y prueba
Para ejecutar nuestra aplicación, simplemente ejecute el métodomain
en la clase RestExampleApplication
. Y para probar los servicios web RESTful, debe descargar un nuevo software) El hecho es que las solicitudes GET son bastante fáciles de enviar desde un navegador normal, pero para POST, PUT y DELETE un navegador normal no es suficiente. No te preocupes: puedes usar Postman para enviar cualquier solicitud HTTP. Puedes descargarlo desde aquí . Después de descargar e instalar, comenzamos a probar nuestra aplicación. Para hacer esto, abra el programa y cree una nueva solicitud: haga clic en el botón Nuevo en la esquina superior izquierda. A continuación, seleccione Solicitar: A continuación, asígnele un nombre y guárdelo. Intentemos ahora enviar una solicitud POST al servidor y crear el primer cliente: Creamos varios clientes de esta manera. Luego cambiamos el tipo de solicitud a GET y la enviamos al servidor:
Resultados generales
Felicitaciones: hemos cubierto bastante el tema de REST. Todo el material resultó voluminoso, pero esperamos que te sea de utilidad:-
Aprendimos qué es DESCANSO.
-
Nos familiarizamos con la historia de REST.
-
Hablamos de las limitaciones y principios de este estilo arquitectónico:
- llevar la arquitectura a un modelo cliente-servidor;
- falta de condición;
- almacenamiento en caché;
- uniformidad de la interfaz;
- capas;
- código bajo demanda (restricción opcional).
-
Examinamos las ventajas que ofrece REST.
-
Examinamos en detalle cómo el servidor y el cliente interactúan entre sí utilizando el protocolo HTTP.
-
Echemos un vistazo más de cerca a las solicitudes y respuestas. Sus componentes fueron desmontados.
-
Finalmente, pasamos a practicar y escribimos nuestra propia pequeña aplicación RESTful en Spring Boot. E incluso aprendimos a probarlo usando el programa Postman.
Tarea
Pruebe lo siguiente:- Siguiendo la descripción anterior, cree su propio proyecto Spring Boot e implemente la misma lógica que en la conferencia. Repite todo 1 en 1.
- Lánzalo. solicitud.
- Descargue y configure Postman (o cualquier otra herramienta para enviar solicitudes, incluso curl).
- Pruebe las solicitudes POST y GET de la misma manera que se describe en la conferencia.
- Pruebe las solicitudes PUT y DELETE usted mismo.
GO TO FULL VERSION