Mira la lección en vídeo
Accede al vídeo completo de esta lección y a más contenido exclusivo con el Plan Plus.
Desbloquear Plan PlusConfiguración de endpoints con @GetMapping
Los endpoints GET representan el punto de entrada más fundamental en cualquier API REST, permitiendo a los clientes recuperar información del servidor de forma segura y sin efectos secundarios. En Spring Boot, la anotación @GetMapping
simplifica enormemente la configuración de estos endpoints, proporcionando una sintaxis clara y expresiva.
Estructura básica de un controlador REST
Un controlador REST en Spring Boot se define mediante la anotación @RestController
, que combina @Controller
y @ResponseBody
. Esta combinación indica que los métodos del controlador devuelven directamente datos en lugar de nombres de vistas:
@RestController
@RequestMapping("/api/productos")
public class ProductoController {
@GetMapping
public List<String> obtenerTodosLosProductos() {
return List.of("Laptop", "Mouse", "Teclado");
}
}
La anotación @RequestMapping
a nivel de clase establece la ruta base para todos los endpoints del controlador. En este ejemplo, cualquier método dentro del controlador tendrá como prefijo /api/productos
.
Configuración de rutas específicas
El método más directo para configurar un endpoint GET es especificar la ruta exacta dentro de la anotación:
@RestController
@RequestMapping("/api")
public class TiendaController {
@GetMapping("/productos")
public List<String> listarProductos() {
return List.of("Producto A", "Producto B", "Producto C");
}
@GetMapping("/categorias")
public List<String> listarCategorias() {
return List.of("Electrónicos", "Ropa", "Hogar");
}
}
Cada método responde a una URL específica: /api/productos
y /api/categorias
respectivamente. Spring Boot automáticamente mapea las peticiones HTTP GET entrantes al método correspondiente basándose en la coincidencia de rutas.
Múltiples rutas para un mismo endpoint
En ocasiones necesitamos que un mismo método responda a diferentes URLs. @GetMapping
permite especificar múltiples rutas mediante un array:
@RestController
public class UsuarioController {
@GetMapping({"/usuarios", "/users", "/clientes"})
public List<String> obtenerUsuarios() {
return List.of("Juan", "María", "Carlos");
}
}
Este enfoque resulta útil para mantener compatibilidad con versiones anteriores de la API o para proporcionar alias más intuitivos según el contexto de uso.
Endpoints con respuestas de objetos
Los controladores REST no se limitan a devolver listas de cadenas. Pueden retornar objetos complejos que Spring Boot serializa automáticamente a JSON:
@RestController
@RequestMapping("/api/tienda")
public class ProductoController {
@GetMapping("/producto-destacado")
public Producto obtenerProductoDestacado() {
return new Producto(1L, "Smartphone", 599.99, "Electrónicos");
}
@GetMapping("/inventario")
public Map<String, Integer> obtenerInventario() {
Map<String, Integer> inventario = new HashMap<>();
inventario.put("Laptops", 15);
inventario.put("Tablets", 8);
inventario.put("Smartphones", 23);
return inventario;
}
}
// Clase auxiliar para el ejemplo
class Producto {
private Long id;
private String nombre;
private Double precio;
private String categoria;
// Constructor, getters y setters
public Producto(Long id, String nombre, Double precio, String categoria) {
this.id = id;
this.nombre = nombre;
this.precio = precio;
this.categoria = categoria;
}
// Getters necesarios para la serialización JSON
public Long getId() { return id; }
public String getNombre() { return nombre; }
public Double getPrecio() { return precio; }
public String getCategoria() { return categoria; }
}
Configuración de headers y tipos de contenido
Aunque @GetMapping
maneja automáticamente la configuración básica, podemos especificar headers adicionales o restricciones de tipo de contenido:
@RestController
public class ApiController {
@GetMapping(value = "/datos", produces = "application/json")
public Map<String, Object> obtenerDatos() {
Map<String, Object> respuesta = new HashMap<>();
respuesta.put("timestamp", System.currentTimeMillis());
respuesta.put("status", "activo");
return respuesta;
}
@GetMapping(value = "/info", headers = "X-API-Version=1.0")
public String obtenerInformacion() {
return "Información de la API versión 1.0";
}
}
El parámetro produces
especifica el tipo MIME de la respuesta, mientras que headers
permite definir restricciones sobre los headers que debe incluir la petición para que el endpoint responda.
Organización de endpoints en controladores
Una buena práctica consiste en agrupar endpoints relacionados en el mismo controlador, manteniendo una estructura lógica y coherente:
@RestController
@RequestMapping("/api/biblioteca")
public class BibliotecaController {
@GetMapping("/libros")
public List<String> obtenerLibros() {
return List.of("El Quijote", "Cien años de soledad", "1984");
}
@GetMapping("/autores")
public List<String> obtenerAutores() {
return List.of("Cervantes", "García Márquez", "Orwell");
}
@GetMapping("/estadisticas")
public Map<String, Integer> obtenerEstadisticas() {
Map<String, Integer> stats = new HashMap<>();
stats.put("totalLibros", 1250);
stats.put("librosPrestados", 89);
stats.put("usuariosActivos", 156);
return stats;
}
}
Esta organización facilita el mantenimiento del código y proporciona una API más intuitiva para los consumidores del servicio.
Parámetros de ruta (@PathVariable)
Los parámetros de ruta permiten capturar valores dinámicos directamente desde la URL, convirtiendo partes específicas de la ruta en variables que nuestros métodos pueden utilizar. Esta funcionalidad resulta esencial para crear APIs REST que manejen recursos específicos mediante identificadores únicos.
Captura básica de parámetros
La anotación @PathVariable
extrae valores de la URL y los inyecta como parámetros del método. La sintaxis utiliza llaves para delimitar las variables en la ruta:
@RestController
@RequestMapping("/api/productos")
public class ProductoController {
@GetMapping("/{id}")
public String obtenerProducto(@PathVariable Long id) {
return "Producto con ID: " + id;
}
@GetMapping("/categoria/{nombre}")
public List<String> obtenerProductosPorCategoria(@PathVariable String nombre) {
return List.of("Producto A de " + nombre, "Producto B de " + nombre);
}
}
En estos ejemplos, las URLs /api/productos/123
y /api/productos/categoria/electronica
capturan automáticamente los valores 123
y electronica
respectivamente.
Múltiples parámetros en una ruta
Una ruta puede contener varios parámetros simultáneamente, permitiendo crear endpoints más específicos y expresivos:
@RestController
@RequestMapping("/api/tienda")
public class TiendaController {
@GetMapping("/categoria/{categoria}/producto/{id}")
public Map<String, Object> obtenerProductoEnCategoria(
@PathVariable String categoria,
@PathVariable Long id) {
Map<String, Object> resultado = new HashMap<>();
resultado.put("categoria", categoria);
resultado.put("productoId", id);
resultado.put("url", "/categoria/" + categoria + "/producto/" + id);
return resultado;
}
@GetMapping("/usuario/{userId}/pedido/{pedidoId}")
public String obtenerPedidoUsuario(
@PathVariable Long userId,
@PathVariable String pedidoId) {
return String.format("Pedido %s del usuario %d", pedidoId, userId);
}
}
El orden de los parámetros en el método no necesita coincidir con el orden en la URL, ya que Spring Boot los mapea por nombre.
Nombres personalizados para variables
Cuando el nombre del parámetro del método difiere del nombre en la URL, podemos especificar explícitamente la correspondencia:
@RestController
@RequestMapping("/api/biblioteca")
public class BibliotecaController {
@GetMapping("/libro/{isbn}")
public String buscarLibro(@PathVariable("isbn") String codigoLibro) {
return "Buscando libro con ISBN: " + codigoLibro;
}
@GetMapping("/autor/{id}/libros")
public List<String> obtenerLibrosAutor(@PathVariable("id") Long autorId) {
return List.of(
"Libro 1 del autor " + autorId,
"Libro 2 del autor " + autorId
);
}
}
Esta flexibilidad permite mantener nombres descriptivos en los métodos mientras se conservan URLs concisas y claras.
Parámetros opcionales y valores por defecto
Spring Boot permite definir parámetros de ruta opcionales mediante el uso de rutas alternativas:
@RestController
@RequestMapping("/api/reportes")
public class ReporteController {
@GetMapping({"/ventas", "/ventas/{periodo}"})
public Map<String, Object> generarReporteVentas(
@PathVariable(required = false) String periodo) {
String periodoFinal = (periodo != null) ? periodo : "mensual";
Map<String, Object> reporte = new HashMap<>();
reporte.put("periodo", periodoFinal);
reporte.put("ventas", 15000);
reporte.put("fecha", System.currentTimeMillis());
return reporte;
}
}
Este enfoque permite que el endpoint responda tanto a /api/reportes/ventas
como a /api/reportes/ventas/anual
, proporcionando un valor por defecto cuando no se especifica el parámetro.
Validación y conversión de tipos
Spring Boot realiza conversión automática de tipos para los parámetros de ruta, manejando tipos primitivos y sus wrappers sin configuración adicional:
@RestController
@RequestMapping("/api/estadisticas")
public class EstadisticaController {
@GetMapping("/usuario/{id}/puntuacion/{valor}")
public String actualizarPuntuacion(
@PathVariable Long id,
@PathVariable Double valor) {
return String.format("Usuario %d tiene puntuación %.2f", id, valor);
}
@GetMapping("/activo/{estado}")
public String verificarEstado(@PathVariable Boolean estado) {
return "Estado activo: " + estado;
}
}
Las URLs como /api/estadisticas/usuario/42/puntuacion/8.5
y /api/estadisticas/activo/true
se procesan correctamente, convirtiendo los valores de cadena a los tipos apropiados.
Patrones complejos con expresiones regulares
Para casos que requieren validación específica del formato, podemos utilizar expresiones regulares dentro de la definición de la ruta:
@RestController
@RequestMapping("/api/sistema")
public class SistemaController {
@GetMapping("/codigo/{codigo:[A-Z]{3}[0-9]{4}}")
public String procesarCodigo(@PathVariable String codigo) {
return "Procesando código válido: " + codigo;
}
@GetMapping("/fecha/{fecha:[0-9]{4}-[0-9]{2}-[0-9]{2}}")
public String obtenerDatosFecha(@PathVariable String fecha) {
return "Datos para la fecha: " + fecha;
}
}
Estos endpoints solo responderán a URLs que cumplan con el patrón especificado, como /api/sistema/codigo/ABC1234
o /api/sistema/fecha/2024-03-15
, rechazando automáticamente formatos incorrectos.
Combinación con otros parámetros
Los parámetros de ruta se integran perfectamente con otros tipos de parámetros, creando endpoints altamente flexibles:
@RestController
@RequestMapping("/api/busqueda")
public class BusquedaController {
@GetMapping("/categoria/{categoria}")
public Map<String, Object> buscarEnCategoria(
@PathVariable String categoria,
@RequestParam(defaultValue = "10") int limite) {
Map<String, Object> resultado = new HashMap<>();
resultado.put("categoria", categoria);
resultado.put("limite", limite);
resultado.put("resultados", List.of("Item 1", "Item 2", "Item 3"));
return resultado;
}
}
Este ejemplo combina un parámetro de ruta obligatorio con un parámetro de consulta opcional, permitiendo URLs como /api/busqueda/categoria/electronica?limite=5
.
Parámetros de consulta (@RequestParam)
Guarda tu progreso
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
Los parámetros de consulta proporcionan una forma flexible de enviar información adicional a nuestros endpoints a través de la URL, utilizando la sintaxis estándar de query strings. La anotación @RequestParam
permite capturar estos valores de manera sencilla y aplicar validaciones automáticas.
Captura básica de parámetros de consulta
Los parámetros de consulta aparecen después del símbolo ?
en la URL y se separan mediante &
. Spring Boot los extrae automáticamente y los inyecta en los métodos del controlador:
@RestController
@RequestMapping("/api/productos")
public class ProductoController {
@GetMapping("/buscar")
public List<String> buscarProductos(@RequestParam String nombre) {
return List.of(
"Producto que contiene: " + nombre,
"Otro producto con: " + nombre
);
}
@GetMapping("/filtrar")
public Map<String, Object> filtrarProductos(
@RequestParam String categoria,
@RequestParam Double precioMinimo) {
Map<String, Object> resultado = new HashMap<>();
resultado.put("categoria", categoria);
resultado.put("precioMinimo", precioMinimo);
resultado.put("total", 25);
return resultado;
}
}
Estos endpoints responden a URLs como /api/productos/buscar?nombre=laptop
y /api/productos/filtrar?categoria=electronica&precioMinimo=100.0
.
Parámetros opcionales con valores por defecto
Una característica fundamental de @RequestParam
es la capacidad de definir parámetros opcionales y establecer valores por defecto cuando no se proporcionan:
@RestController
@RequestMapping("/api/catalogo")
public class CatalogoController {
@GetMapping("/listar")
public Map<String, Object> listarProductos(
@RequestParam(defaultValue = "1") int pagina,
@RequestParam(defaultValue = "10") int tamaño,
@RequestParam(defaultValue = "nombre") String ordenarPor) {
Map<String, Object> respuesta = new HashMap<>();
respuesta.put("pagina", pagina);
respuesta.put("tamaño", tamaño);
respuesta.put("ordenarPor", ordenarPor);
respuesta.put("productos", List.of("Producto A", "Producto B"));
return respuesta;
}
@GetMapping("/ofertas")
public List<String> obtenerOfertas(
@RequestParam(required = false) String categoria,
@RequestParam(defaultValue = "false") boolean soloActivas) {
String filtro = categoria != null ? " en " + categoria : "";
String estado = soloActivas ? " (solo activas)" : " (todas)";
return List.of("Ofertas" + filtro + estado);
}
}
El endpoint /api/catalogo/listar
funciona tanto sin parámetros como con cualquier combinación de ellos: /api/catalogo/listar?pagina=2&tamaño=5
.
Nombres personalizados para parámetros
Cuando necesitamos que el nombre del parámetro en la URL difiera del nombre de la variable en Java, podemos especificar el mapeo explícitamente:
@RestController
@RequestMapping("/api/reportes")
public class ReporteController {
@GetMapping("/ventas")
public Map<String, Object> generarReporteVentas(
@RequestParam("fecha_inicio") String fechaInicio,
@RequestParam("fecha_fin") String fechaFin,
@RequestParam(value = "incluir_impuestos", defaultValue = "true") boolean incluirImpuestos) {
Map<String, Object> reporte = new HashMap<>();
reporte.put("periodo", fechaInicio + " - " + fechaFin);
reporte.put("conImpuestos", incluirImpuestos);
reporte.put("total", 15750.50);
return reporte;
}
}
Este enfoque permite mantener convenciones de nomenclatura diferentes entre la API pública y el código interno, respondiendo a URLs como /api/reportes/ventas?fecha_inicio=2024-01-01&fecha_fin=2024-01-31
.
Manejo de múltiples valores para un parámetro
Los parámetros de consulta pueden recibir múltiples valores para la misma clave, útil para implementar filtros complejos o selecciones múltiples:
@RestController
@RequestMapping("/api/inventario")
public class InventarioController {
@GetMapping("/productos")
public Map<String, Object> buscarProductos(
@RequestParam List<String> categorias,
@RequestParam(required = false) List<String> marcas) {
Map<String, Object> resultado = new HashMap<>();
resultado.put("categoriasFiltradas", categorias);
resultado.put("marcasFiltradas", marcas != null ? marcas : List.of());
resultado.put("productosEncontrados", 42);
return resultado;
}
@GetMapping("/estadisticas")
public Map<String, Integer> obtenerEstadisticas(
@RequestParam String[] meses) {
Map<String, Integer> stats = new HashMap<>();
for (String mes : meses) {
stats.put(mes, (int) (Math.random() * 1000));
}
return stats;
}
}
Estos endpoints procesan URLs como /api/inventario/productos?categorias=electronica&categorias=hogar&marcas=samsung&marcas=lg
.
Conversión automática de tipos
Spring Boot realiza conversión automática entre los valores de cadena de la URL y los tipos Java correspondientes, incluyendo tipos primitivos, enums y colecciones:
@RestController
@RequestMapping("/api/configuracion")
public class ConfiguracionController {
@GetMapping("/usuario")
public Map<String, Object> configurarUsuario(
@RequestParam Long userId,
@RequestParam boolean notificaciones,
@RequestParam Double descuento,
@RequestParam(defaultValue = "BASICO") TipoUsuario tipo) {
Map<String, Object> config = new HashMap<>();
config.put("usuario", userId);
config.put("notificacionesActivas", notificaciones);
config.put("descuentoAplicable", descuento);
config.put("tipoUsuario", tipo);
return config;
}
}
enum TipoUsuario {
BASICO, PREMIUM, VIP
}
La URL /api/configuracion/usuario?userId=123¬ificaciones=true&descuento=15.5&tipo=PREMIUM
se procesa automáticamente con las conversiones apropiadas.
Validación y manejo de errores
Podemos aplicar validaciones básicas directamente en la definición de los parámetros para garantizar la integridad de los datos:
@RestController
@RequestMapping("/api/pedidos")
public class PedidoController {
@GetMapping("/buscar")
public Map<String, Object> buscarPedidos(
@RequestParam(required = true) String estado,
@RequestParam(defaultValue = "1") int pagina,
@RequestParam(defaultValue = "20") int limite) {
// Validación manual del rango
if (limite > 100) {
limite = 100;
}
if (pagina < 1) {
pagina = 1;
}
Map<String, Object> resultado = new HashMap<>();
resultado.put("estado", estado);
resultado.put("paginaActual", pagina);
resultado.put("elementosPorPagina", limite);
resultado.put("pedidos", List.of("Pedido 1", "Pedido 2"));
return resultado;
}
}
Combinación con Map para parámetros dinámicos
Para casos donde no conocemos de antemano todos los parámetros posibles, podemos capturar todos los parámetros de consulta en un Map:
@RestController
@RequestMapping("/api/busqueda")
public class BusquedaController {
@GetMapping("/avanzada")
public Map<String, Object> busquedaAvanzada(
@RequestParam Map<String, String> parametros) {
Map<String, Object> resultado = new HashMap<>();
resultado.put("parametrosRecibidos", parametros);
resultado.put("totalParametros", parametros.size());
// Procesamiento específico de parámetros conocidos
if (parametros.containsKey("q")) {
resultado.put("terminoBusqueda", parametros.get("q"));
}
return resultado;
}
@GetMapping("/flexible")
public Map<String, Object> busquedaFlexible(
@RequestParam(required = false) String categoria,
@RequestParam Map<String, String> filtrosAdicionales) {
Map<String, Object> respuesta = new HashMap<>();
respuesta.put("categoriaBase", categoria);
respuesta.put("filtrosPersonalizados", filtrosAdicionales);
return respuesta;
}
}
Este enfoque resulta especialmente útil para APIs flexibles que necesitan adaptarse a diferentes tipos de consultas sin modificar el código del controlador.
Respuestas JSON automáticas gracias a Jackson
Spring Boot integra automáticamente la biblioteca Jackson para manejar la serialización y deserialización de objetos Java a formato JSON, eliminando la necesidad de configuración manual en la mayoría de casos. Esta integración transparente permite que nuestros controladores REST devuelvan objetos complejos que se convierten automáticamente en respuestas JSON bien formateadas.
Serialización automática de objetos
Cuando un método de controlador devuelve un objeto Java, Jackson se encarga de convertirlo a JSON utilizando las propiedades públicas y los métodos getter de la clase:
@RestController
@RequestMapping("/api/empleados")
public class EmpleadoController {
@GetMapping("/{id}")
public Empleado obtenerEmpleado(@PathVariable Long id) {
return new Empleado(id, "Ana García", "Desarrolladora", 45000.0);
}
@GetMapping
public List<Empleado> listarEmpleados() {
return List.of(
new Empleado(1L, "Carlos López", "Analista", 42000.0),
new Empleado(2L, "María Rodríguez", "Diseñadora", 38000.0)
);
}
}
class Empleado {
private Long id;
private String nombre;
private String puesto;
private Double salario;
public Empleado(Long id, String nombre, String puesto, Double salario) {
this.id = id;
this.nombre = nombre;
this.puesto = puesto;
this.salario = salario;
}
// Getters necesarios para Jackson
public Long getId() { return id; }
public String getNombre() { return nombre; }
public String getPuesto() { return puesto; }
public Double getSalario() { return salario; }
}
La respuesta JSON generada automáticamente tendrá la estructura:
{
"id": 1,
"nombre": "Ana García",
"puesto": "Desarrolladora",
"salario": 45000.0
}
Personalización de nombres de propiedades
Jackson permite personalizar los nombres de las propiedades en el JSON resultante mediante la anotación @JsonProperty
, útil para seguir convenciones de nomenclatura específicas:
class Producto {
private Long id;
private String nombre;
private Double precio;
private String categoria;
private LocalDateTime fechaCreacion;
public Producto(Long id, String nombre, Double precio, String categoria) {
this.id = id;
this.nombre = nombre;
this.precio = precio;
this.categoria = categoria;
this.fechaCreacion = LocalDateTime.now();
}
public Long getId() { return id; }
@JsonProperty("product_name")
public String getNombre() { return nombre; }
@JsonProperty("price_amount")
public Double getPrecio() { return precio; }
public String getCategoria() { return categoria; }
@JsonProperty("created_at")
public LocalDateTime getFechaCreacion() { return fechaCreacion; }
}
@RestController
@RequestMapping("/api/productos")
public class ProductoController {
@GetMapping("/{id}")
public Producto obtenerProducto(@PathVariable Long id) {
return new Producto(id, "Smartphone Galaxy", 699.99, "Electrónicos");
}
}
Control de visibilidad de propiedades
Podemos controlar qué propiedades se incluyen en la respuesta JSON utilizando anotaciones específicas de Jackson:
class Usuario {
private Long id;
private String email;
private String password;
private String nombre;
private String rol;
private boolean activo;
public Usuario(Long id, String email, String password, String nombre, String rol) {
this.id = id;
this.email = email;
this.password = password;
this.nombre = nombre;
this.rol = rol;
this.activo = true;
}
public Long getId() { return id; }
public String getEmail() { return email; }
public String getNombre() { return nombre; }
public String getRol() { return rol; }
@JsonIgnore
public String getPassword() { return password; }
@JsonProperty("is_active")
public boolean isActivo() { return activo; }
}
@RestController
@RequestMapping("/api/usuarios")
public class UsuarioController {
@GetMapping("/{id}")
public Usuario obtenerUsuario(@PathVariable Long id) {
return new Usuario(id, "usuario@email.com", "secreto123", "Juan Pérez", "ADMIN");
}
}
La anotación @JsonIgnore
excluye la propiedad password
del JSON de respuesta, manteniendo la seguridad de datos sensibles.
Formateo de fechas y números
Jackson proporciona opciones para formatear automáticamente fechas y números según patrones específicos:
class Evento {
private Long id;
private String titulo;
private LocalDateTime fechaInicio;
private LocalDate fechaFin;
private BigDecimal precio;
private Integer capacidad;
public Evento(Long id, String titulo, LocalDateTime fechaInicio,
LocalDate fechaFin, BigDecimal precio, Integer capacidad) {
this.id = id;
this.titulo = titulo;
this.fechaInicio = fechaInicio;
this.fechaFin = fechaFin;
this.precio = precio;
this.capacidad = capacidad;
}
public Long getId() { return id; }
public String getTitulo() { return titulo; }
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public LocalDateTime getFechaInicio() { return fechaInicio; }
@JsonFormat(pattern = "yyyy-MM-dd")
public LocalDate getFechaFin() { return fechaFin; }
@JsonFormat(shape = JsonFormat.Shape.STRING)
public BigDecimal getPrecio() { return precio; }
public Integer getCapacidad() { return capacidad; }
}
@RestController
@RequestMapping("/api/eventos")
public class EventoController {
@GetMapping("/{id}")
public Evento obtenerEvento(@PathVariable Long id) {
return new Evento(
id,
"Conferencia Spring Boot",
LocalDateTime.of(2024, 6, 15, 9, 0),
LocalDate.of(2024, 6, 15),
new BigDecimal("150.00"),
200
);
}
}
Manejo de objetos anidados y colecciones
Jackson serializa automáticamente objetos complejos que contienen otros objetos o colecciones, manteniendo la estructura jerárquica:
class Direccion {
private String calle;
private String ciudad;
private String codigoPostal;
public Direccion(String calle, String ciudad, String codigoPostal) {
this.calle = calle;
this.ciudad = ciudad;
this.codigoPostal = codigoPostal;
}
public String getCalle() { return calle; }
public String getCiudad() { return ciudad; }
public String getCodigoPostal() { return codigoPostal; }
}
class Cliente {
private Long id;
private String nombre;
private Direccion direccion;
private List<String> telefonos;
private Map<String, String> metadatos;
public Cliente(Long id, String nombre, Direccion direccion) {
this.id = id;
this.nombre = nombre;
this.direccion = direccion;
this.telefonos = List.of("123-456-789", "987-654-321");
this.metadatos = Map.of("preferencia", "email", "idioma", "es");
}
public Long getId() { return id; }
public String getNombre() { return nombre; }
public Direccion getDireccion() { return direccion; }
public List<String> getTelefonos() { return telefonos; }
public Map<String, String> getMetadatos() { return metadatos; }
}
@RestController
@RequestMapping("/api/clientes")
public class ClienteController {
@GetMapping("/{id}")
public Cliente obtenerCliente(@PathVariable Long id) {
Direccion direccion = new Direccion("Calle Mayor 123", "Madrid", "28001");
return new Cliente(id, "Elena Martínez", direccion);
}
}
Respuestas con códigos de estado HTTP
Podemos combinar la serialización automática con códigos de estado HTTP específicos utilizando ResponseEntity
:
@RestController
@RequestMapping("/api/pedidos")
public class PedidoController {
@GetMapping("/{id}")
public ResponseEntity<Pedido> obtenerPedido(@PathVariable Long id) {
if (id <= 0) {
return ResponseEntity.badRequest().build();
}
Pedido pedido = new Pedido(id, "Pedido de prueba", EstadoPedido.PROCESANDO);
return ResponseEntity.ok(pedido);
}
@GetMapping("/{id}/resumen")
public ResponseEntity<Map<String, Object>> obtenerResumenPedido(@PathVariable Long id) {
Map<String, Object> resumen = new HashMap<>();
resumen.put("pedidoId", id);
resumen.put("total", 299.99);
resumen.put("articulos", 3);
resumen.put("estado", "ENVIADO");
return ResponseEntity.ok()
.header("X-Total-Count", "1")
.body(resumen);
}
}
class Pedido {
private Long id;
private String descripcion;
private EstadoPedido estado;
public Pedido(Long id, String descripcion, EstadoPedido estado) {
this.id = id;
this.descripcion = descripcion;
this.estado = estado;
}
public Long getId() { return id; }
public String getDescripcion() { return descripcion; }
public EstadoPedido getEstado() { return estado; }
}
enum EstadoPedido {
PENDIENTE, PROCESANDO, ENVIADO, ENTREGADO
}
Configuración global de Jackson
Spring Boot permite personalizar el comportamiento de Jackson a nivel global mediante propiedades de configuración en application.properties
:
# Formato de fechas por defecto
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=Europe/Madrid
# Comportamiento con propiedades nulas
spring.jackson.default-property-inclusion=NON_NULL
# Formato de nombres de propiedades
spring.jackson.property-naming-strategy=SNAKE_CASE
# Manejo de propiedades desconocidas
spring.jackson.deserialization.fail-on-unknown-properties=false
Estas configuraciones afectan a todos los endpoints de la aplicación, proporcionando un comportamiento consistente en toda la API sin necesidad de anotar cada clase individualmente.
Aprendizajes de esta lección de SpringBoot
- Comprender la configuración básica de endpoints GET usando @GetMapping en Spring Boot.
- Aprender a capturar y utilizar parámetros de ruta con @PathVariable.
- Manejar parámetros de consulta mediante @RequestParam, incluyendo valores opcionales y múltiples.
- Entender la serialización automática de objetos Java a JSON con Jackson en respuestas REST.
- Aplicar buenas prácticas en la organización de controladores y personalización de respuestas JSON.
Completa este curso de SpringBoot y certifícate
Únete a nuestra plataforma de cursos de programación y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs