En el desarrollo web hay dos direcciones en las que se mueven los datos:
- Cliente → Servidor (procesamiento de peticiones).
- Servidor → Cliente (formación de respuestas).
Si ya hemos visto la primera dirección, la segunda merece nuestra atención. Vamos a ver cómo el servidor puede "responder" al cliente y por qué eso importa para que la interacción funcione.
Opciones de respuesta
- Páginas HTML.
- El servidor devuelve código HTML que el navegador renderiza para el usuario.
- JSON/XML.
- Datos serializados para clientes (por ejemplo en una REST API).
- Archivos u otros recursos.
- Por ejemplo, imágenes, PDF o archivos ZIP.
En Spring MVC podemos formar respuestas de forma flexible gracias a su potente sistema de anotaciones e instrumentos.
Anotación @ResponseBody
Si vienes del mundo de las REST API, la frase "respuestas JSON" te sonará a música. Esta anotación se encarga de enviar datos en formato JSON o XML desde el controlador al cliente.
En esencia, @ResponseBody le dice a Spring: "Basta de hacer esos espectáculos bonitos con páginas HTML. Simplemente manda los datos al cliente, sin dramatismo". Es decir, cancela la resolución de vistas (view resolution), y en su lugar devuelve los datos directamente en la respuesta HTTP.
Ejemplo: Envío de respuesta JSON
Aquí tienes el ejemplo más básico:
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/user")
public User getUser() {
return new User(1, "John Doe");
}
}
Aquí usamos @RestController, que en esencia es un atajo de @Controller + @ResponseBody. Eso significa que cada método devuelve una respuesta JSON. Si devuelves un objeto, Spring lo transformará automáticamente a JSON (serializará) gracias a la librería Jackson integrada.
¿Cómo funciona? Spring sabe serializar objetos a JSON gracias a la librería Jackson. Está incluida "out of the box", así que no necesitas movimientos extra. Pero si quieres serializar a XML, tendrás que hacer algún ajuste.
Ejemplo: Uso de @ResponseBody sin @RestController
Si por alguna razón estás usando un controlador normal en vez de @RestController, no pasa nada. Simplemente añade @ResponseBody al método.
@Controller
@RequestMapping("/api")
public class UserController {
@ResponseBody
@GetMapping("/user")
public User getUser() {
return new User(1, "Jane Doe");
}
}
Esta anotación indica a Spring que los datos deben devolverse directamente, y no buscar una vista.
¿Se puede devolver algo más que JSON?
Claro. Si quieres devolver, por ejemplo, una cadena u otro tipo de dato, @ResponseBody lo hará con gusto.
@Controller
@RequestMapping("/api")
public class ExampleController {
@ResponseBody
@GetMapping("/hello")
public String sayHello() {
return "Hello, Spring MVC!";
}
}
El cliente recibirá simplemente la cadena: Hello, Spring MVC!.
Errores típicos al usar @ResponseBody
- Error: "Cannot serialize object". Esto sucede si el objeto no puede convertirse a JSON. Por ejemplo, no tiene
publicgetters, o te olvidaste de incluir Jackson. - Devolver HTML por accidente. Si olvidas
@ResponseBodyo usas@Controllersin ella, Spring puede intentar buscar una página HTML cuyo nombre coincida con el valor devuelto (por ejemplo, "user" para el métodogetUser).
Anotación @ModelAttribute
Si @ResponseBody es tu mejor amigo para las REST API, @ModelAttribute es imprescindible para trabajar con el MVC clásico, donde se usan HTML y plantillas del lado del servidor.
La anotación @ModelAttribute te ayuda a pasar datos del controlador a la vista. Carga un objeto en el modelo (sí, nos referimos a org.springframework.ui.Model!), para luego pasarlo a la plantilla.
Ejemplo: pasar un objeto a la vista
Supongamos que tenemos una página HTML que muestra la información de un usuario. Primero hay que pasar los datos:
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/profile")
public String userProfile(Model model) {
User user = new User(1, "Jane Doe");
model.addAttribute("user", user);
return "userProfile";
}
}
Aquí añadimos el objeto user al modelo y luego devolvemos el nombre de la vista (userProfile), que lo mostrará. La vista puede ser, por ejemplo, una plantilla Thymeleaf.
Uso de @ModelAttribute
¿Pero para qué agregar manualmente al modelo si existe @ModelAttribute?
@Controller
@RequestMapping("/users")
public class UserController {
@ModelAttribute("user")
public User getUser() {
return new User(1, "Jane Doe");
}
@GetMapping("/profile")
public String userProfile() {
return "userProfile";
}
}
Spring llamará al método con @ModelAttribute automáticamente en cualquier petición al controlador, y el objeto se añadirá al modelo con el nombre indicado (user). Esto reduce el código y mantiene tu controlador más limpio.
Ejemplo: trabajar con formularios
@ModelAttribute también se usa para enlazar los datos del formulario a objetos Java.
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/register")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
@PostMapping("/register")
public String processRegistration(@ModelAttribute("user") User user) {
System.out.println("Registered user: " + user);
return "registrationSuccess";
}
}
- Petición GET muestra el formulario de registro con un objeto
Uservacío. - Petición POST enlaza automáticamente los datos del formulario con el objeto
User.
Errores típicos al usar @ModelAttribute
- Error: "NullPointerException". Si te olvidas de añadir el objeto al modelo, la vista no podrá renderizar los datos.
- Error de enlace de datos. Si tienes un objeto complejo y los datos del formulario no pueden asignarse, Spring lanzará un error. Asegúrate de que todos los campos del formulario coincidan con los campos del objeto.
¿Cuándo usar @ResponseBody y @ModelAttribute?
| Anotación | Cuándo usar |
|---|---|
@ResponseBody |
Cuando necesites devolver JSON/XML o una cadena |
@ModelAttribute| Cuando trabajas con formularios HTML o plantillas |
Ahora estás armado con los conocimientos sobre cómo formar respuestas en Spring MVC. Recuerda: @ResponseBody es la mejor opción para REST API, y @ModelAttribute te salvará en el mundo de formularios y la generación de HTML del lado del servidor.
GO TO FULL VERSION