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
|
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.
|
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
|
La versión 3 de Spring Boot se lanzó en noviembre de 2022 y algunas de sus
|
||||||
nuevas características principales son:
|
nuevas características principales son:
|
||||||
|
|
||||||
@ -110,3 +109,221 @@ api
|
|||||||
└── pom.xml
|
└── 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)
|
- [JDBC](./010_spring_boot/jdbc.md)
|
||||||
- [Persistencia con JPA - Hibernate](./010_spring_boot/jpa_persistencia_hibernate.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)
|
- [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