CRUD con Spring MVC Portlet (III): Añadiendo validación al formulario

3
13101

CRUD con Spring MVC Portlet (III): Añadiendo validación al formulario

0. Índice de
contenidos.

1. Entorno

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Mac Book Pro 17″ (2,6 Ghz Intel Core
    i7, 8 GB
    DDR3)
  • Sistema Operativo: Mac OS X Snow Leopard 10.6.4
  • Spring MVC Portlet 3.0.4
  • Maven 2.2.1
  • Eclipse 3.6 (Helios) con M2Eclipse
  • Liferay 6.0.5

2. Introducción

Este tutorial es la tercera parte de una serie, por lo que te recomiendo que antes leas:

En esta tercera parte vamos a ver cómo añadir validación a nuestro formulario de entrada de datos y cómo realizar pruebas unitarias de nuestro portlet, a fin de poder probar la lógica del portlet sin necesidad de arrancar el gestor de portales.

3. Añadiendo validación al formulario

Para añadir validación a nuestro formulario de entrada de datos con Spring tenemos que implementar un tipo de servicio que se conoce como Validator. Que no es más que una clase que implementa la interfaz Validator de Spring.

Para nuestro caso, queremos que todos los campos sean obligatorios y que no podamos dar de alta a ninguna persona cuyo nombre sea «Pepe» (No es que tenga nada en contra de las personas que se llamen «Pepe», es sólo un ejemplo)

package com.autentia.springcrudmvcportlet.validators;

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

import com.autentia.springcrudmvcportlet.forms.PersonaForm;

@Service
public class PersonaFormValidator implements Validator{

	private static final String NOMBRE_NO_PERMITIDO = "Pepe";
	
	public boolean supports(Class<?> clazz) {
		return PersonaFormValidator.class.isAssignableFrom(clazz);
	}

	public void validate(Object obj, Errors errors) {
		PersonaForm personaForm = (PersonaForm) obj;
		validateNombre(personaForm, errors);
		validateApellidos(personaForm, errors);
		validateDireccion(personaForm, errors);
	}
	
	private void validateNombre(PersonaForm personaForm, Errors errors){
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "nombre", 
				"errors.required", new String[]{"Nombre"}, "El campo 'Nombre' es obligatorio defecto");
		if (NOMBRE_NO_PERMITIDO.equals(personaForm.getNombre())){
			errors.rejectValue("nombre", "persona.nombre-no-permitido", 
					new String[]{NOMBRE_NO_PERMITIDO}, 
					new StringBuilder("Nombre no permitido '").append(NOMBRE_NO_PERMITIDO).append("'").toString());
		}
	}
	
	private void validateApellidos(PersonaForm personaForm, Errors errors){
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "apellidos", 
				"errors.required", new String[]{"Apellidos"}, "El campo 'Apellidos' es obligatorio defecto");
	}
	
	private void validateDireccion(PersonaForm personaForm, Errors errors){
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "direccion", 
				"errors.required", new String[]{"Dirección" }, "El campo 'Dirección' es obligatorio defecto");
		
	}
	
}

Esta interfaz nos obliga a implementar dos métodos: supports y validate.

El primero de ellos establece si el validador puede validar instancias de la clase sumimistrada, una implementación típica de este método la podemos encontrar en el ejemplo de arriba.

El método validate es el que va a llevar a cabo todas las validaciones que consideremos necesarias para nuestro formulario. Es una buena práctica dividir este método en al menos tantas funciones como campos queremos validar de nuestro formulario.

Para la validación de algo tan típico como determinar si el campo de entrada está vacío, podemos utilizar la clase ValidationUtils de Spring, donde podemos determinar el nombre del campo afectado, que mensaje internacionalizado e incluso parametrizado queremos mostrar, y en caso de no encontrar la clave del mensaje, poder mostrar un texto por defecto.

Para validaciones más específicas, como comprobaciones en base de datos o en cualquier otra fuente de datos, siempre podemos contar con el método Errors.rejectValue() como hemos visto en el ejemplo.

Después vamos a añadir los mensajes de error utilizados en nuestro fichero messages.properties.

errors.required=El campo ''{0}'' es obligatorio
persona.nombre-no-permitido=Nombre no permitido ''{0}''

Ahora editamos el fichero detalle_persona.jsp para incluir el código que muestra los errores.





<portlet:defineObjects />


	<portlet:param name="accion" value="${futuraAccion}" />



	<form:hidden path="idPersona"/>
	<form:errors path="*"/>
	

<spring:message code="persona.nombre" /> <form:input path="nombre" /> <form:errors path="nombre"/>

<spring:message code="persona.apellidos" /> <form:input path="apellidos" /> <form:errors path="apellidos"/>

<spring:message code="persona.direccion" /> <form:input path="direccion" /> <form:errors path="direccion"/>

<input type="submit" value="${formEnviar}"/> <input type="submit" value="${formCancelar}" name="form.cancelar"/>

Como vemos, utilizamos la etiqueta <form:errors/> podemos mostrar el conjunto de errores que detecta nuestro validador estableciendo el atributo path a *, o los podemos mostrar asociados a su campo, estableciendo en el atributo path el nombre del campo.

Por último, añadimos la lógica de validación a nuestro controlador. En nuestro caso, los únicos puntos de entrada de datos que necesitan validación son la inserción y la modificación que quedarían de esta forma:

@RequestMapping(params = {"javax.portlet.action=ejecutarAccion", "accion=crud.alta"})
	protected final void insertarPersona(
			@ModelAttribute("personaForm") PersonaForm personaForm, BindingResult result,
			@RequestParam(required=false, value="form.cancelar") String cancelar, ModelMap model, 
			ActionResponse response, @RequestParam("accion") String accion) {
		
		if (siUsuarioNoCancelaAccion(cancelar)) {
			personaFormValidator.validate(personaForm, result);
			if (!result.hasErrors()){
				personaDAO.insert(personaForm.getPersona());
				model.addAttribute("msgstatus", messages.getMessage("status.ok", null, LocaleContextHolder.getLocale()));
			}else{
				model.addAttribute("futuraAccion", accion);
				response.setRenderParameter("redirect", "irADetalle");
			}
		}
	}
	
	@RequestMapping(params = {"javax.portlet.action=ejecutarAccion", "accion=crud.modificacion"})
	protected final void modificarPersona(
			@ModelAttribute("personaForm") PersonaForm personaForm, BindingResult result,
			@RequestParam(required=false, value="form.cancelar") String cancelar, ModelMap model,
			ActionResponse response, @RequestParam("accion") String accion) {
		
		if (siUsuarioNoCancelaAccion(cancelar)) {
			personaFormValidator.validate(personaForm, result);
			if (!result.hasErrors()){
				personaDAO.update(personaForm.getPersona());
				model.addAttribute("msgstatus", messages.getMessage("status.ok", null, LocaleContextHolder.getLocale()));
			}else{
				model.addAttribute("futuraAccion", accion);
				response.setRenderParameter("redirect", "irADetalle");	
			}
		}
	}

En los dos métodos hacemos lo mismo. Llamamos al validador el cual mediante el método hasErrors() nos dice si ha habido algún error de validación. Si lo ha habido, hacemos un redirect a la pantalla de detalle volviendo a establecer el valor de futuraAccion para que no se pierda.

Hemos añadido más elementos de entrada a las especificaciones del método. El más importante es BindingResult donde Spring almacena el resultado de validación del formulario y que obligatoriamente debe ir definido justo después de la definición del formulario, exactamente como se muestra en el ejemplo.

Ahora cuando añadimos o modificamos datos debemos encontrarnos con mensajes de este estilo.


4. Conclusiones

Ahora ya tenemos nuestro formulario a prueba de bombas gracias a los validadores de Spring. En el próximo tutorial de este serie veremos como realizar pruebas unitarias a este desarrollo.

Cualquier duda o sugerencia en la zona de comentarios.

Saludos.

3 COMENTARIOS

  1. Me funciona casi perfecto ;d
    Si hay errores no vuelve al detalle sino que vuelve a listado_personas, asi que mi pregunta es ¿irADetalle donde se especifica?
    response.setRenderParameter(\\\»redirect\\\», \\\»irADetalle\\\»);

  2. Hola Resaca_man y Rubén,

    El código de irADetalle sería el siguiente:

    @RequestMapping(params=\\\»redirect=irADetalle\\\»)
    protected String irADetalle(){
    return \\\»detalle_persona\\\»;
    }

    Espero que os sea de ayuda.

    Saludos.

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