viernes, 21 de agosto de 2009

Spring MVC Usando @Controller y Formularios

En el post anterior vimos como podemos usar el estereotipo @Controller para mapear las solicitudes con la anotación @RequestMapping a métodos, esta vez vamos a ver como podemos usar formularios, validarlos y hacer uso de una clase estereotipada(denuevo @Controller) que controle y mapee adecuadamente las solicitudes que se hagan con el formulario, para esto la configuración de la aplicación no ha cambiado(no he modificado web.xml ni frontController-servlet.xml del post anterior y que voy a reutilizar).

Veamos el primer archivo cliente:
<a href="${pageContext.request.contextPath}/forms/usuario.do">Registrar Usuario</a>

La línea anterior la podemos colocar en cualquier archivo jsp y lo que hará es solicitar al controlador(UsuarioController.java) que me retorne la página donde voy a registrar al usuario, la clase estereotipada UsuarioController.java es la siguiente:


package pe.com.slcsccy.springmvc.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import pe.com.slcsccy.springmvc.dominio.Usuario;
import pe.com.slcsccy.springmvc.validacion.UsuarioValidador;

@Controller
@RequestMapping("/forms/usuario.do")
@SessionAttributes(types = Usuario.class)
public class UsuarioController {

/**
Este método es el que se ejecutará cuando las solicitudes tengan el patrón URL de la forma /forms/usuario.do y sean del tipo GET, es decir desde un link a href, desde un URL, desde javascript o cuando en el formulario html el atributo method sea GET o no esté especificado.
*/
@RequestMapping(method = RequestMethod.GET)
public String inicializarFormulario(Model model) {
/*
Al formulario le enviamos los datos por default de un usuario, este método actua como el inicializador o constructor del formulario
*/
Usuario usuario = new Usuario();
model.addAttribute(usuario);
return "forms/usuario";
}

/*
Este método será ejecutado solo cuando el patrón URL del formulario sea /forms/usuario.do y además el método del formulario que envía los datos solo sea POST, el detalle de los argumentos de los métodos es el siguiente:
@param @ModelAttribute contiene todos los datos del usuario que ha sido enviado desde el formulario
@param BindingResult, BindinResult es una interface, lo que hace Spring es crear un objeto que implementa esta interface,especificamente Spring implementa org.springframework.validation.BeanPropertyBindingResult este objeto se enlazará el objeto usuario enviado para permitir realizar las validaciones necesarias, extiende de Errors.
@param SessionStatus, objeto que determina si el PROCESAMIENTO de la session a sido completado o no, podemos usar este objeto para indicar a la aplicación que el PROCESAMIENTO de la session ha sido completado y que los atributos en él ya no son necesarios.
*/
@RequestMapping(method = RequestMethod.POST)
public String procesarFormulario(@ModelAttribute Usuario usuario,
BindingResult resultado, SessionStatus estado) {
/* Validamos los datos del usuario */
new UsuarioValidador().validar(usuario, resultado);

/* Si no hay errores limpiamos la session y redireccionamos */
if(!resultado.hasErrors()){
/* Limpiamos los objetos de session para este procesamiento*/
estado.setComplete();
/* redireccionamos al método inicializarFormulario */
return "redirect:usuario.do";
}

/* Si hay errores los mostramos en el formulario */
return "forms/usuario";
}

}


La clase anterior usa un validador que es el siguiente:

UsuarioValidador.java
package pe.com.slcsccy.springmvc.validacion;

import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import pe.com.slcsccy.springmvc.dominio.Usuario;

public class UsuarioValidador {

public void validar(Usuario usuario, Errors errors) {

if (!StringUtils.hasLength(usuario.getCodigo())) {
errors.rejectValue("codigo", "required", "required");
}
if (!StringUtils.hasLength(usuario.getNombres())) {
errors.rejectValue("nombres", "required", "required");
}
if (!StringUtils.hasLength(usuario.getApellidos())) {
errors.rejectValue("apellidos", "required", "required");
}
}
}


Espero haber sido lo suficientemente claro en los comentarios java, si tienes dudas no dudes en comentar.

La clase anterior será llamada cuando el usuario hace click en un enlace como:
<a href="${pageContext.request.contextPath}/forms/usuario.do">Registrar Usuario</a>

La clase será llamada porque el url(href) satisface el patrón declarado en la clase:
@RequestMapping("/forms/usuario.do") y el método a ejecutar será inicializarFormulario, porque dicho método está anotado para que le lleguen todas las solicitudes GET @RequestMapping(method = RequestMethod.GET) debes de recordar que una de las formas de realizar solicitudes GET es mediante links(href) en las páginas html.

Una vez ejecutado el método, este instanciarà un objeto usuario y lo devolverá en el modelo de datos a la siguiente vista:

usuario.jsp
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Registro de Usuarios</title>
</head>
<body>
<form:form modelAttribute="usuario">
Codigo: <form:input path="codigo" /><form:errors path="codigo"/><br/>
Nombres: <form:input path="nombres" /><form:errors path="nombres"/><br/>
Apellidos: <form:input path="apellidos" /><form:errors path="apellidos"/><br/><br/><br/>
<input type="submit" value="Registrar Usuario"/>
</form:form>
</body>
</html>


Podemos ver el código html generado y notar que el atributo method(del formulario) es POST, entonces cuando le demos al botón "Registrar Usuario" este procesamiento será enviado hacia el método procesarFormulario de la clase UsuarioController.

Eso es todo por el momento, si tienes alguna duda puedes resolverla comentando...

4 comentarios:

  1. Muy bien explicado, creo que lo he entendido todo bien. Voy a probar el código a ver si me termino de aclarar.

    Muchas gracias! :D

    ResponderBorrar
  2. Hola amigo, muy buen post, está muy bien explicado. Tengo una duda, como puedo mandar un formulario por el metodo GET? Algo así como para un buscador. Gracias

    ResponderBorrar
  3. ¿¿Y cómo eliminamos el resultado de las validaciones del BindingResult, para que no vuelvan a aparecer si recargamos la página??

    ResponderBorrar
  4. Hola, me gustaria saber si sabes como llenar un formulario por medio del GET en base a un objeto que tengo dentro de una lista. Saludos

    ResponderBorrar

Es bueno comunicarnos, comenta!!.