Implementación de un API REST con Spring
Spring MVC y @RestController
Sección titulada «Spring MVC y @RestController»Spring MVC es la tecnología que usamos para construir servicios web que siguen la arquitectura REST. Una clase anotada con @RestController agrupa las implementaciones correspondientes a cada verbo HTTP (GET, POST, PUT, DELETE) asociado a cada URI de un recurso. Por ejemplo, una clase controlador puede reunir todos los servicios que se ofrecen para el recurso Book.
Cuando un cliente HTTP invoca un servicio, por ejemplo POST /books, ocurre lo siguiente:
- La petición
POST /bookses procesada del lado del servidor por Spring. - Spring identifica el endpoint: el método asociado con la ruta
/booksy con el verboPOST. - El método se ejecuta.
- Al terminar, la respuesta se transforma al formato del protocolo HTTP y se envía de regreso al cliente en la representación elegida por el desarrollador —normalmente JSON.
En la petición viaja un objeto JSON; cuando Spring la procesa, el método correspondiente recibe un objeto Java (un DTO, por ejemplo BookDTO). Recorre el siguiente ejemplo para ver el camino completo de una petición a través de las tres capas:
Recorre una petición completa
Cambia de operación y sigue el dato desde el cliente hasta la base de datos y de regreso.
GET /api/artistas/1Enviar GET
El cliente solicita el artista con identificador 1.
GET /api/artistas/1Implementación de las representaciones de los recursos
Sección titulada «Implementación de las representaciones de los recursos»Mientras que el cliente de los servicios REST utiliza representaciones JSON de los recursos, la implementación de esos recursos usa objetos de una clase Java. Las instancias de esa clase solo tienen los valores de los atributos y se suelen llamar POJO (Plain Old Java Object). Por convención, cada clase Java que representa un recurso se llama RecursoDTO, donde Recurso es el nombre del recurso: para el recurso Book, la clase es BookDTO.
Cómo se convierte el JSON en un DTO (y viceversa)
Sección titulada «Cómo se convierte el JSON en un DTO (y viceversa)»Nadie escribe a mano el código que lee cada propiedad del JSON y la asigna al atributo correspondiente del DTO. Esa conversión la hace un mapper: una librería que, dados un JSON y una clase Java, empareja cada propiedad con el atributo del mismo nombre. En Java, la librería estándar para esto es Jackson, y su clase principal es ObjectMapper.
Explora el ejemplo: un EstudianteDTO con su carnet, su nombre y la lista de cursos que aprobó, donde cada curso es a su vez otro objeto con su propio nombre.
JSON
{
"carnet": 12345,
"carne": 12345,
"nombre": "Camila Ruiz",
"cursosAprobados": [
{ "nombre": "Bases de Datos" },
{ "nombre": "Ingeniería de Software" }
]
}Java (DTOs)
@Data @NoArgsConstructor @AllArgsConstructor
public class EstudianteDTO {
private Long carnet;
private String nombre;
private List<CursoDTO> cursosAprobados;
}
public class CursoDTO {
private String nombre;
}Haz clic en una propiedad del JSON o en un atributo de la clase Java para ver con cuál se corresponde.
✅ Con estos nombres, el mapper asigna 12345 a EstudianteDTO.carnet.
⚠️ Con "carne" en el JSON, el mapper no encuentra una propiedad llamada carnet: el atributo queda en null, sin ningún error ni advertencia.
Implementación de una clase recurso
Sección titulada «Implementación de una clase recurso»Supongamos que queremos implementar los servicios REST de la página de diseño para una entidad City: crear una ciudad, obtener todas las ciudades, obtener una por id, actualizarla y borrarla.
| Método | Path | Acción | Parámetros | Cuerpo | Retorno |
|---|---|---|---|---|---|
| GET | /cities |
Lista los registros de City |
Colección de City |
||
| GET | /cities/:id |
Obtiene una instancia de City con identificador id |
@PathVariable id |
Atributos de la instancia | |
| POST | /cities |
Crea una instancia nueva de City |
Atributos de la instancia a crear | Instancia creada, con su id | |
| PUT | /cities/:id |
Actualiza la instancia con identificador id |
@PathVariable id |
Objeto JSON de City |
Instancia actualizada |
| DELETE | /cities/:id |
Borra la instancia con identificador id |
@PathVariable id |
La clase CityController tendrá entonces los siguientes métodos:
| Método | Descripción |
|---|---|
List<CityDTO> findAll() |
Retorna la lista de ciudades. |
CityDTO create(CityDTO city) |
Crea una ciudad con la información recibida por parámetro. |
CityDTO findOne(Long id) |
Retorna la ciudad identificada con id. |
CityDTO updateCity(Long id, CityDTO city) |
Actualiza la información de la ciudad identificada con id. |
void delete(Long id) |
Borra la ciudad identificada con id. |
Para que Spring procese una clase como recurso, esta debe estar anotada: @RestController indica que la clase es un controlador, y @RequestMapping indica el path del recurso.
Anotaciones para indicar el verbo HTTP
Sección titulada «Anotaciones para indicar el verbo HTTP»| Anotación | Método | Descripción |
|---|---|---|
@GetMapping |
List<CityDTO> findAll() |
Retorna la lista de ciudades. |
@PostMapping |
CityDTO create(CityDTO city) |
Crea una ciudad con la información recibida por parámetro. |
@GetMapping |
CityDTO findOne(Long id) |
Retorna la ciudad identificada con id. |
@PutMapping |
CityDTO update(Long id, CityDTO city) |
Actualiza la ciudad id con la información de city. |
@DeleteMapping |
void delete(Long id) |
Borra la ciudad identificada con id. |
Anotación para indicar el path
Sección titulada «Anotación para indicar el path»Cuando definimos el servicio GET /cities, cities es el path. Cuando todos los métodos de una clase recurso comparten el mismo path inicial, ese path se anota al comienzo de la clase:
@RequestMapping("/cities")public class CityController { // ...}Cuando el path necesita más información que la del comienzo de la clase —por ejemplo GET /cities/id, donde id es el identificador numérico de la ciudad— se anota directamente sobre el método:
@GetMapping(value = "/{id}")public CityDTO findOne(@PathVariable("id") Long id) throws EntityNotFoundException { // ...}@PathVariable("id") conecta el segmento {id} de la ruta con el parámetro id del método: Spring lo extrae del path y lo convierte al tipo declarado, en este caso Long.