Implementando nuestro propio formulario de validación con Spring MVC.

4
43972

0. Índice de contenidos.

1. Introducción

En este tutorial vamos a ver cómo implementar nuestro propio validador para los datos de un formulario con Spring MVC.

Existen determinados casos en que las validaciones de los datos de un formulario son tan sencillas como validar que cierto campo sea obligatorio o que otro no pueda ser mayor de cierto valor. En este caso, podríamos solucionar el problema con anotaciones del tipo @NotNull o @Size en nuestro bean.
Sin embargo, existen otras ocasiones en que las validaciones de unos datos pueden estar condicionadas por otros. En este caso, una buena solución podría ser definir nuestro propio validador para el formulario.

2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2.2 Ghz Intel Core I7, 4GB DDR3).
  • Sistema Operativo: Mac OS Snow Leopard 10.6.7
  • Entorno de desarrollo: Eclipse 3.6.2.
  • Apache Tomcat 6.0.32 con jdk 1.6.
  • Spring 3.1.0.
  • Navegador: Mozilla Firefox 4.0.1

3. Diseñar los datos del formulario.

Lo primero que debemos hacer es definir el bean en el que llegarán los datos al controlador directamente desde el cliente. En nuestro caso, el bean tendrá información relativa a un coche, en concreto: matrícula, modelo, año y kilómetros.

public class DatosCoche {
	
	private String matricula;
	
	private String modelo;
	
	private int anho;
		
	private int kilometros;

	// getters y setters	
		
}

4. Implementar el validador concreto.

En este punto, estamos ya en disposición de poder implementar nuestro validador concreto. Las validaciones que realizaremos serán siguientes:

  • La matrícula es obligatoria.
  • La matrícula debe ser de la forma NNNN-LLL o L-NNNN-LL; donde L = letra y N = número.
  • El modelo es obligatorio.
  • El año no puede ser inferior a 1900 ni superior al año actual.
  • Si el año no es el actual, los kilómetros deben estar comprendidos entre 0 y 100.000.

No se si esto tiene mucho sentido en una aplicación empresarial, pero para nuestro ejemplo nos vale ;).

El validador quedaría de la siguiente manera:

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;


public class ValidadorDatosCoche implements Validator {
	
	private static final int AÑO_ACTUAL = 
		Integer.valueOf(new SimpleDateFormat("yyyy").format(new Date()));	

	@Override
	public boolean supports(Class<?> clazz) {		
		return DatosCoche.class.equals(clazz); // clase del bean al que da soporte este validador
	}

	@Override
	public void validate(Object target, Errors errors) {
		
		DatosCoche datosCoche = (DatosCoche) target;
		
		// la matrícula es obligatoria
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "matricula", "field.matricula.required", 
				"La matrícula es obligatoria");
		
		// debe tener un formato correcto del tipo: 1111-BBB o B-2222-MM
		validarFormatoMatricula(datosCoche.getMatricula(), errors); // valida la matricula por expresión regular
		
		// la matrícula es obligatoria
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "modelo", "field.modelo.required", 
			"El modelo es obligatorio");
		
		// el año debe ser válido no puede ser mayor que el actual
		if ( datosCoche.getAnho() < 1900 || datosCoche.getAnho() > AÑO_ACTUAL ) {
			errors.rejectValue("anho", "field.anho.invalid", "El anho es incorrecto");
		}	
		
		// si no hay errores relacionados con el campo año
		if ( ! errors.hasFieldErrors("anho")) {
		
			// para los coches de año distinto al actual, validamos que no tengan más de 100.000 km
			if ( datosCoche.getAnho() < AÑO_ACTUAL ) {
				
				if ( datosCoche.getKilometros() < 0 ) {
					errors.rejectValue("kilometros", "field.kilometros.invalid", 
							"Los kilómetros son incorrectos");
				}
				
				if ( datosCoche.getKilometros() > 100000 ) {
					errors.rejectValue("kilometros", "field.kilometros.toomany", 
							"No se aceptan coches de más de 100000 km");
				}
			
			}		
		}		
		
	}
		
	private void validarFormatoMatricula (String matricula, Errors errors) {
		// valida la matrícula por expresión regular, si hay error lo añade a errors
	}

}

El método rejectIfEmptyOrWhitespace de la clase ValidationUtils, será el encargado de añadir el error. En nuestro ejemplo recibe 4 parámetros: los errores, el nombre del campo que no pasó la validación, la clave del mensaje (internacionalización, properties…) y el mensaje por defecto.

El método rejectValue de la clase Errors sirve para lo mismo.

5. El controlador.

A continuación, creamos el controlador.

El método manejarFormularioYaValidado será el que reciba nuestro formulario con el resultado del proceso de validación. Al principio preguntamos si hubo errores con los datos del formulario y, en ese caso, redirigimos a la vista del formulario (vistaFormularioCoche.jsp). En caso de que todo haya salido correctamente ya se pueden manejar los datos, sabiendo que son correctos. Podríamos llamar a un Service que los tratara y redirigir a donde fuese.

El método verFormulario únicamente nos redirecciona a la vista del formulario.

Con el método initBinder estamos registrando el validador concreto que acabamos de implementar.

El método populateForm nos suministra el objeto en el que se cargarán los datos del formulario, que posteriormente serán validados. En nuestro caso nos proporcionará un DatosCoche.

El código sería este:

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@Controller
@RequestMapping("/formularioCoche")
public class ControladorDatosCoche {

	@RequestMapping(method = RequestMethod.GET)
	public String verFormulario () {				
		return "vistaFormularioCoche"; // nos redirige a la vista del formulario
	}
	
	@InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new ValidadorDatosCoche()); // registramos el validador
    }
	
	@RequestMapping(value = "/manejar", method = RequestMethod.POST)
    public String manejarFormularioYaValidado(@Valid DatosCoche datosCoche, 
    		BindingResult result) {
		
		// si hay errores volvemos a la vista del formulario
		if ( result.hasErrors() ) {
			return "vistaFormularioCoche";
		}
		
		// si no hay errores, manejamos los datos validados...
		
		return "vistaQueSea";
		
	}
	
	@ModelAttribute("datosCoche")
	public DatosCoche populateForm() {
	     return new DatosCoche(); // creamos el bean para que se pueda popular
	}
	
}

6. Crear la vista.

Por último nos faltaría crear la jsp vistaFormularioCoche.jsp donde se define el formulario con el que interactuará el usuario. Debemos prestar especial atención a los siguientes elementos:

  • form:form define el formulario que vamos a popular y validar. El valor de su atributo commandName debe coincidir con el de la anotación @ModelAttribute del método populateForm de nuestro controlador.
  • form:input define cada uno de los campos que vamos a cargar en nuestro DatosCoche. El atributo path hace referencia a la propiedad a cargar.
  • form:errors será el componente (un span de HTML) donde aparecerá el error relacionado con el campo (path) que lleve definido. El atributo cssClass referencia al la regla de estilo css que le queramos dar.

La jsp vistaFormularioCoche.jsp quedaría de la siguiente forma:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html xmlns="http://www.w3.org/1999/xhtml">

<head>
	<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
	<title>AUTENTIA - Prueba de Formulario de validación con Spring MVC 3.1</title>
	<style type="text/css">
		body {
			font-family: verdana, sans-serif;			
		}
		
		span.campoConError {
			color: red;
		}
		
	</style>	
</head>

<body>
				
	<c:url value="/formularioCoche/manejar" var="destino"/>			
	<form:form method="post" action="${destino}" commandName="datosCoche">

Formulario con datos del coche

Matrícula: <form:input path="matricula" /> <form:errors path="matricula" cssClass="campoConError"/>
Modelo: <form:input path="modelo" /> <form:errors path="modelo" cssClass="campoConError"/>
Año: <form:input path="anho" maxlength="4" /> <form:errors path="anho" cssClass="campoConError"/>
Kilómetros: <form:input path="kilometros" maxlength="6" /> <form:errors path="kilometros" cssClass="campoConError"/>
<input type="submit" value="Enviar" />
</form:form> </body> </html>

El formulario se vería de la siguiente forma:

Introduciendo datos incorrectos nos mostraría lo siguiente:

7. Referencias.

8. Conclusiones.

Hemos visto que es relativamente sencillo implementar nuestro propio validador utilizando Spring MVC.

El validador concreto nos aportará más control que otro sistema de validación a modo de anotaciones, sin embargo, con éste último no tendremos que implementar nada, por lo que puede que si las validaciones que vamos a realizar a los campos de nuestro formulario son constantes y sencillas, tengamos que pensar en esta segunda opción como más adecuada.

Espero que este tutorial os haya sido de ayuda. Un saludo.

Miguel Arlandy

marlandy@autentia.com

4 COMENTARIOS

  1. Hola;
    Tenga Ud un lindo día, una consulta quería saber si es lo mismo trabajar con netBeans 8.0 o es que cambia por completo la sintaxis a seguir? y si es posible si fuese muy amable dar validación a capos que contengan Combobox

  2. Buenas, compañero, he realizado tu ejemplo y efectivamente el formulario no se envia cuando los campos validados estan vacios. Solo use dos atributos(usuario, clave) en el service para hacerlo mas facil. Pero resulta que no quiere mostrar los mensajes asignados en la clase que implementa la interface Validator. Realizo los mismos paso que usted, pero no me salen los mensajes en el formulario. Tendra algo que ver el parametro «field.nombreCampoFormulario.request» ? Saludos!

  3. Saludos, mi consulta es la siguiente: Estoy usando Spring mvc en eclipse juntamente con Boostrap. Cuando uso los tag «form» para la etiqueta del formulario se emite errores impidiendo integrar boostrap a la aplicacion. El siguiente error es el siguiente:

    mensaje Ha sucedido una excepción al procesar la página JSP [/WEB-INF/views/home.jsp] en línea [65]

    descripción El servidor encontró un error interno que hizo que no pudiera rellenar este requerimiento.

    excepción

    org.apache.jasper.JasperException: Ha sucedido una excepción al procesar la página JSP [/WEB-INF/views/home.jsp] en línea [65]

    62:
    63:
    64:
    65:
    66:
    67:
    68:

    Atte,
    Cesar

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad