Validación personalizada Spring MVC

En tutoriales anteriores aprendimos a validar la entrada de los formularios usando el validador de Hibernate con las anotaciones correspondientes, una alternativa diferente es crear nuestra propia lógica de validación para un formulario en especifico, para utilizar esta funcionalidad necesitamos la anotación @InitBinder y además implementar la interface Validator.

Validar formulario usando la interface Validator

Primero agregamos las dependencias necesarias.

<dependencies>
    <!-- Spring Web MVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <!-- Java Servlet y JSP -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>  
    
    <!-- WebJars -->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>        
        <version>3.2.0</version>
    </dependency>
    
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>webjars-locator</artifactId>
        <version>0.31</version>
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.8.8</version>
    </dependency>
    
    <!-- Materialize -->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>materializecss</artifactId>
        <version>0.98.2</version>
    </dependency>
    
</dependencies>

Adicionalmente hemos agregado las dependencias requeridas para utilizar los webjars ya que de este modo podremos utilizar el Framework CSS Materialize para darle estilo y diseño a nuestro formulario, esto es opcional.

El modelo que utilizaremos para la demostración de la validación está definido por la clase que representa una persona, es esta:

public class Person {

    private String nombre;
    private String apellido;
    private String correo;
    private String nacionalidad;
    private Integer edad;
    // ...
}

Para validar este modelo es necesario crear una clase que implemente la interface Validator, debemos sobre-escribir los siguientes dos métodos:

public class PersonValidator implements Validator {

    @Override
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }

    @Override
    public void validate(Object o, Errors errors) {
        // ...
    }
}
  • supports: indica la clase a la que el validador le da soporte.
  • validate: realiza la validación, si hay errores registra los correspondientes mensajes.

La validación la realizamos con la clase ValidationUtils e implementando nuestra propia lógica.

@Override
public void validate(Object obj, Errors errors) {

    Person person = (Person) obj;

    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "nombre", "person.nombre", "El nombre no puede estar vacio.");
    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "apellido", "person.apellido", "El apellido no puede estar vacio.");

    String correo = person.getCorreo();
    if (correo == null || !correo.endsWith("@mail.com.pa")) {
        errors.rejectValue("correo", "person.correo", "El correo no es valido, debe terminar en @mail.com.pa.");
    }

    Integer edad = person.getEdad();
    if (edad == null || edad < 0) {
        errors.rejectValue("edad", "person.edad", "Debes indicar la edad y no puede ser negativa.");
    }

    String nacionalidad = person.getNacionalidad();
    if (nacionalidad == null || nacionalidad.equals("...")) {
        errors.rejectValue("nacionalidad", "person.nacionalidad", "Indica tu pais de nacimiento o recidencia.");
    }
}

El método rejectIfEmptyOrWhitespace(...) lanza un error si el campo indicado está vacío o contiene solo espacios en blanco, los parámetros son los siguientes, primero la variable en donde se almacenan los errores, luego el nombre del campo que se valida, sigue la clave usada para localizar el mensaje de error en un archivo de propiedades, al final el mensaje de error que se usa si no se encuentra la clave.

Usando el método rejectValue(...) generamos un error si un campo no cumple con una determinada condición, por ejemplo, si la edad es menor que cero generamos el error, los parámetros son similares a lo explicado previamente.

El controlador valida el formulario del mismo modo que los explicamos en el correspondiente tutorial, lo nuevo es el método initBinder(...) anotado con @InitBinder la tarea del mismo es registrar el validador que acabamos de crear.

@Controller
public class PersonController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new PersonValidator());
    }

    @RequestMapping(path = {"/", "/form"})
    public ModelAndView home() {
        return new ModelAndView("home", "person", new Person());
    }

    @RequestMapping(path = {"/validar"}, method = RequestMethod.POST)
    public String validar(@Validated Person person, BindingResult result) {

        if (result.hasErrors()) {
            return "home";
        }

        return "valid";
    }
}

El método home() es el encargado de llamar a la vistas JSP del mismo nombre, esta es la que genera el formulario por lo que le pasamos un objeto Person como atributo para que sea rellenado por el usuario a través del formulario.

Por otra parte validar(...) es invocado cuando presionamos el botón enviar del formulario utilizando HTTP POST, la anotación @Validated indica que el objeto debe ser validado, si detectamos un error volvemos a la página para mostrar los mensajes de error y procesar nuevamente el formulario.

Código JSP que genera el formulario, utilizamos materializecss para el estilo y diseño, este framework requiere JQuery.

<%@ page contentType="text/html" pageEncoding="UTF-8" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="core" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>

<html>
    <head> 
        <title>Validar Formulario</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <link href="static/materializecss/css/materialize.min.css" type="text/css" rel="stylesheet" />  

        <style type="text/css">
            .input-error { margin-bottom: 6px !important; }
            .text-error { font-size: 0.8em !important; }
        </style>

    </head>
    <body>
        <div class="container">

            <div class="row">
                <h3 class="indigo-text center">Formulario de registro</h3>
            </div>

            <div class="row">

                <core:url value="/validar" var="validarUrl"/>
                <form:form method="POST" modelAttribute="person" action="${validarUrl}">

                    <form:errors path="*" cssClass="card-panel red lighten-2" element="div" />

                    <div class="row">
                        <div class="input-field col s6">
                            <form:label  path="nombre" >Nombre</form:label>   
                            <form:input  path="nombre" cssErrorClass="invalid input-error" />   
                            <form:errors path="nombre" element="div" cssClass="red-text text-error"/>                             
                        </div>

                        <div class="input-field col s6">
                            <form:label  path="apellido" >Apellido</form:label>   
                            <form:input  path="apellido" cssErrorClass="invalid input-error" />   
                            <form:errors path="apellido" element="div" cssClass="red-text text-error"/>  
                        </div>
                    </div>

                    <div class="row">
                        <div class="input-field col s12">
                            <form:label  path="correo" >Correo</form:label>   
                            <form:input  path="correo" cssErrorClass="invalid input-error" />   
                            <form:errors path="correo" element="div" cssClass="red-text text-error"/>  
                        </div>
                    </div>
                        
                    <div class="row">
                        <div class="input-field col s12">
                            <form:label  path="edad" >Edad</form:label>
                            <form:input  path="edad" cssErrorClass="invalid input-error" />
                            <form:errors path="edad" element="div" cssClass="red-text text-error" />  
                        </div>
                    </div> 
                        
                    <div class="row">
                        <div class="input-field col s12">
                            <form:select path="nacionalidad" items="${paisList}" cssErrorClass="invalid input-error" />
                            <form:errors path="nacionalidad" element="div" cssClass="red-text text-error"/>  
                        </div>
                    </div>

                    <div class="row">
                        <div class="input-field col">
                            <input type="submit" value="Enviar" class="btn waves-effect"/>
                        </div>
                    </div>

                </form:form>

            </div>
        </div>

        <script src="static/jquery/jquery.min.js"></script>
        <script src="static/materializecss/js/materialize.min.js"></script>       

        <script type="text/javascript" >
            $(document).ready(function () {
                $('select').material_select();
            });
        </script>
    </body>
</html>

Si deseas saber más sobre los formularios JSP dirígete a Spring MVC Formularios, el resultado es el siguiente:

Validación personalizada de formularios en Spring MVC

Con esto terminamos el tutorial de validación personalizada de formularios, usamos las tecnologías JSP, JQuery y Materializecss para la creación del mismo, estas dos últimas son opcionales, las utilizamos solamente para mejorar el diseño.

Descargar proyecto: personalizar-validación.zip

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Entrenar OpenCV en Detección de Objetos

Detección de figuras geométricas

Procesamiento de imágenes en OpenCV

Conociendo la clase cv::Mat de OpenCV