Desarrollo API Rest: Aula 2
- mapeo de solicitudes POST en clase Controller - solicitudes POST a la API usando Insomnia - datos a la API en formato JSON - anotación @RequestBody para recibir datos del cuerpo de la solicitud en un parámetro en el Controller - patrón DTO con Java Records, representando datos recibidos en un POST.
This commit is contained in:
parent
d4e5da3592
commit
41f062fdd7
@ -0,0 +1,15 @@
|
||||
package med.voll.api.controller;
|
||||
|
||||
import med.voll.api.medico.DatosRegistroMedico;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/medicos")
|
||||
public class MedicoController {
|
||||
|
||||
@PostMapping
|
||||
public void registrarMedico(@RequestBody DatosRegistroMedico datosRegistroMedico) {
|
||||
System.out.println(datosRegistroMedico);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package med.voll.api.direccion;
|
||||
|
||||
public record DatosDireccion(String calle, String distrito, String cuidad, String numero, String complemento) {
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package med.voll.api.medico;
|
||||
|
||||
import med.voll.api.direccion.DatosDireccion;
|
||||
|
||||
public record DatosRegistroMedico(String nombre, String email, String documento,
|
||||
Especialidad especialidad, DatosDireccion direccion) {
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package med.voll.api.medico;
|
||||
|
||||
public enum Especialidad {
|
||||
ORTOPEDIA,
|
||||
CARDIOLOGIA,
|
||||
GINECOLOGIA,
|
||||
PEDIATRIA
|
||||
}
|
BIN
010_spring_boot/imgs/spring_boot_cors_error.png
Normal file
BIN
010_spring_boot/imgs/spring_boot_cors_error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
@ -66,7 +66,6 @@ El lanzamiento de Spring Boot fue un hito para el desarrollo de aplicaciones
|
||||
Java, ya que hizo más simple y ágil esta tarea, facilitando mucho la vida de
|
||||
las personas que utilizan el lenguaje Java para desarrollar sus aplicaciones.
|
||||
|
||||
|
||||
La versión 3 de Spring Boot se lanzó en noviembre de 2022 y algunas de sus
|
||||
nuevas características principales son:
|
||||
|
||||
@ -110,3 +109,221 @@ api
|
||||
└── pom.xml
|
||||
```
|
||||
|
||||
|
||||
### Utilizando Insomnia
|
||||
|
||||
AppImage de [Insomnia](https://github.com/Kong/insomnia/releases/) para probar API
|
||||
|
||||
Test post a `http://127.0.0.1:8080/medicos`, **json**
|
||||
|
||||
```json
|
||||
{
|
||||
"nombre": "Rodrigo Lopez",
|
||||
"email": "rodrigo.lopez@voll.med",
|
||||
"documento": "123456",
|
||||
"especialidad": "ortopedia",
|
||||
"direccion": {
|
||||
"calle": "calle 1",
|
||||
"distrito": "distrito 1",
|
||||
"ciudad": "Lima",
|
||||
"numero": "1",
|
||||
"complemento": "a"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### JSON
|
||||
|
||||
**JSON** (JavaScript Object Notation) es un formato utilizado para representar
|
||||
información, al igual que **XML** y **CSV**.
|
||||
|
||||
Una API necesita recibir y devolver información en algún formato que represente
|
||||
los recursos que administra. **JSON** es uno de estos posibles formatos, popular
|
||||
por su ligereza, sencillez, facil lectura (humana y máquina), así como por su
|
||||
soporte para diferentes lenguajes de programación.
|
||||
|
||||
Representación de información en formato **XML**
|
||||
|
||||
```xml
|
||||
<producto>
|
||||
<nombre>Mochila</nombre>
|
||||
<precio>89.90</precio>
|
||||
<descripcion>Mochila para notebooks de hasta 17 pulgadas</descripcion>
|
||||
</producto>
|
||||
```
|
||||
|
||||
Representación en formato **JSON**
|
||||
|
||||
```json
|
||||
{
|
||||
“nombre” : “Mochila”,
|
||||
“precio” : 89.90,
|
||||
“descripcion” : “Mochila para notebooks de hasta 17 pulgadas”
|
||||
}
|
||||
```
|
||||
|
||||
Observe cómo el formato JSON es mucho más compacto y legible. Precisamente por eso, se ha convertido en el formato universal utilizado en la comunicación de aplicaciones, especialmente en el caso de las API REST.
|
||||
|
||||
Más detalles sobre [JSON](https://www.json.org/json-es.html)
|
||||
|
||||
#### CORS
|
||||
|
||||
Al desarrollar una API y se busca que sus recursos estén disponibles para
|
||||
cualquier ***cliente HTTP***.
|
||||
|
||||
**CORS** (Cross-Origin Resource Sharing) o “Intercambio de recursos con
|
||||
diferentes orígenes”. Es común tener errores CORS al consumir y poner a
|
||||
disposición una API.
|
||||
|
||||
![img](./imgs/spring_boot_cors_error.png)
|
||||
|
||||
**CORS** es un mecanismo utilizado para agregar encabezados HTTP que indica a
|
||||
los navegadores permitir que una aplicación web se ejecute en un origen y acceda
|
||||
a los recursos desde un origen diferente. Este tipo de acción se denomina
|
||||
***cross-origin HTTP request***.
|
||||
En la práctica, les informa a los navegadores si se puede acceder o no a un
|
||||
recurso en particular.
|
||||
|
||||
Entendiendo los errores
|
||||
|
||||
***`Same-origin policy`***
|
||||
Por defecto, una aplicación Front-end, escrita en JavaScript, solo puede acceder
|
||||
a los recursos ubicados en el mismo origen de la solicitud. Esto sucede debido
|
||||
a la política del mismo origen (*same-origin policy*), que es un mecanismo de
|
||||
seguridad de los navegadores que restringe la forma en que un documento o script
|
||||
de un origen interactúa con los recursos de otro.
|
||||
Esta política tiene como objetivo detener los ataques maliciosos.
|
||||
|
||||
Dos URL comparten el mismo origen si el **protocolo**, el **puerto** (si se
|
||||
especifica) y el **host** son los mismos. Comparemos posibles variaciones
|
||||
considerando la URL `https://cursos.alura.com.br/category/programacao`:
|
||||
|
||||
| URL | Resultado | Motivo |
|
||||
| - | - | - |
|
||||
| `https://cursos.alura.com.br/category/front-end` | Mismo origen | Solo camino diferente |
|
||||
| `http://cursos.alura.com.br/category/programacao` |Error de CORS | Protocolo diferente (http) |
|
||||
| `https://faculdade.alura.com.br:80/category/programacao` | Error de CORS | Host diferente |
|
||||
|
||||
#### ¿Como consumir una API con una URL diferente sin tener problemas CORS?
|
||||
|
||||
Por ejemplo, si se quiere consumir una API que se ejecuta en el puerto `8000` desde
|
||||
una aplicación React corriendo en el puerto `3000`.
|
||||
|
||||
Al enviar una solicitud a una API de origen diferente, la API debe devolver un
|
||||
header llamado `Access-Control-Allow-Origin`. Dentro de esta, es ella es necesario
|
||||
informar los diferentes orígenes que serán permitidas de consumir la API, en
|
||||
este caso: `Access-Control-Allow-Origin: http://localhost:3000`.
|
||||
|
||||
Para permitir el acceso desde cualquier origen se utiliza el símbolo `*`:
|
||||
`Access-Control-Allow-Origin: *`. Esta es una medida **no recomendada**, ya que
|
||||
permite que fuentes desconocidas accedan al servidor, a menos que sea intencional,
|
||||
como en el caso de una API pública.
|
||||
|
||||
**¿Cómo hacer esto en Spring Boot correctamente?**
|
||||
|
||||
#### Habilitando diferentes orígenes en Spring Boot
|
||||
|
||||
Configurar el **CORS** para permitir que un origen específico consuma la API,
|
||||
creando una clase de configuración como la sgte.
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class CorsConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins("http://localhost:3000")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE",
|
||||
"OPTIONS", "HEAD", "TRACE", "CONNECT");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`http://localhost:3000` sería la dirección de la aplicación Front-end y
|
||||
`.allowedMethods` los métodos que se permitirán ejecutar. Con esto se podrá
|
||||
consumir la API sin problemas desde una aplicación front-end.
|
||||
|
||||
#### Restricciones o validaciones
|
||||
|
||||
|| Médico ||
|
||||
| :- | :- | :- |
|
||||
| Nombre | Solo letras | No puede llegar vacío |
|
||||
| Especialidad | Ortopedia, Ginecología,<br>Cardiología, Pediatria | No puede llegar vacío |
|
||||
| Documento | Solo números | No puede llegar vacío |
|
||||
| Email | Formato de email | No puede llegar vacío |
|
||||
| Teléfono | Solo números | No puede llegar vacío |
|
||||
|
||||
|| Dirección ||
|
||||
| :- | :- | :- |
|
||||
| Calle | Letras y números | No puede llegar vacío |
|
||||
| Número | Solo números | No puede llegar vacío |
|
||||
| Complemento | Letras y números | |
|
||||
| Cuidad | Letras y números | No puede llegar vacío |
|
||||
|
||||
### Java Record
|
||||
|
||||
Lanzado oficialmente en Java 16, pero disponible experimentalmente desde Java 14.
|
||||
**Record** es un recurso que ***permite representar una clase inmutable, que
|
||||
contiene solo atributos, constructor y métodos de lectura***, de una manera muy
|
||||
simple y ágil.
|
||||
|
||||
Este tipo de clase encaja perfectamente para representar **clases DTO**, ya que
|
||||
su objetivo es únicamente representar datos que serán recibidos o devueltos por
|
||||
la API, sin ningún tipo de comportamiento.
|
||||
|
||||
Para crear una clase DTO inmutable, sin la utilización de Record, era necesario
|
||||
escribir mucho código. El sgte. es un ejemplo de una clase DTO que representa
|
||||
un teléfono:
|
||||
|
||||
```java
|
||||
public final class Telefono {
|
||||
|
||||
private final String ddd;
|
||||
private final String numero;
|
||||
|
||||
public Telefono(String ddd, String numero) {
|
||||
this.ddd = ddd;
|
||||
this.numero = numero;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(ddd, numero);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
} else if (!(obj instanceof Telefono)) {
|
||||
return false;
|
||||
} else {
|
||||
Telefono other = (Telefono) obj;
|
||||
return Objects.equals(ddd, other.ddd)
|
||||
&& Objects.equals(numero, other.numero);
|
||||
}
|
||||
}
|
||||
|
||||
public String getDdd() {
|
||||
return this.ddd;
|
||||
}
|
||||
|
||||
public String getNumero() {
|
||||
return this.numero;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Con **Record** todo ese código se puede resumir en una sola línea:
|
||||
|
||||
```java
|
||||
public record Telefono(String ddd, String numero){}
|
||||
```
|
||||
|
||||
Internamente, Java transforma este registro en una clase inmutable, muy similar
|
||||
al código que se muestra arriba.
|
||||
|
||||
Documentación Java
|
||||
[record](https://docs.oracle.com/en/java/javase/17/language/records.html)
|
||||
|
||||
|
@ -50,3 +50,4 @@ primoridiales en programación con Javascript
|
||||
- [JDBC](./010_spring_boot/jdbc.md)
|
||||
- [Persistencia con JPA - Hibernate](./010_spring_boot/jpa_persistencia_hibernate.md)
|
||||
- [JPA consultas avanzadas, rendimiento y modelos complejos](./010_spring_boot/jpa_avanzado.md)
|
||||
- [Desarrollo API Rest](./010_spring_boot/spring_boot_1.md)
|
||||
|
Loading…
Reference in New Issue
Block a user