Validadores de la capa de presentación

RECU-0141 (Recurso Referencia)

Descripción

JSF, dispone de varios controles para la introducción de datos: campos de texto (ocultos, normales o de contraseña), áreas de texto y controles de selección, tanto múltiple como individual. Todos ellos aceptan varias formas de validación de los datos introducidos.

  • Existen propiedades autónomas de la etiqueta del control que pueden cargarse como true o false para adaptar el comportamiento del control cuando se realiza el envío (summit) del formulario.
  • Es posible que exista un método que realiza la validación de los datos introducidos en el control. Este método se encuentra en el bean de respaldo (backbean) o de otro que exista en el alcance de la página. En este caso, el método se indica mediante una expresión EL de JSF en una de las propiedades del control.
  • Invocación de un método validate del objeto que implementa el control (binding)
  • De igual forma, existen etiquetas específicas para asignar un cierto validador a un control. Esta etiqueta se crea como hija del control, permitiéndose más de una. Esta es la forma de añadir los validadores a los que por defecto proporciona JSF y los que se hayan creado en la aplicación como clases independientes.

Todos estos diferentes métodos de añadir validaciones a un cierto control no son incompatibles entre ellos, pudiendo aparecer todos en un mismo control. Una vez que la página ha sido enviada, los validadores asociados a un control son invocados y el control se considerará válido cuando todos sus validadores sean válidos. Si el proceso de validación falla, los datos no se cargan en el modelo de datos permitiendo a la fase de validación que genere la respuesta al usuario. Cada método de validación, en el caso de que no considere válido el valor del control, debe crear una instancia de FacesMessage en donde incluirá un mensaje alusivo. Este FacesMessage se rodeará de una excepción ValidatorException que será lanzada por el método. Luego será responsabilidad del resto del ciclo de vida mostrar al usuario de forma apropiada los mensajes de error.

Uso de validadores en JSF

La librería de etiquetas de la especificación dispone de los siguientes sistemas para el uso de validadores :

  • La propiedad required en los controles. Esta indica la necesidad o no de datos en el control en el que se indique, lo que se entiende como una forma de validación de datos. Toma valores “true” o “false”. En la especificación se dice que si no se indica de forma explícita y el control correspondiente se deja vacío, no se ejecutarán el resto de los procesos de validación que el control pueda tener definido.
<h:inputText id=’valor’ required=”true” />
  • Validadores básicos, incluidos en la especificación de JSF, que se declararán como hijos de la etiqueta que defina el control.
  • Métodos de validación. Se indica en la propiedad validator del correspondiente control el método que se invocará para la validación. Este método puede ser del propio bean de respaldo o de otro bean que se encuentre en el alcance. Este sistema está orientado a validaciones específicas a nivel de aplicación.
<h:inputText id="emailInput"
            validator="#{registrationBean.validateEmail}"
            value="#{registrationBean.email}/>
  • Validadores a medida creados para realizar validaciones específicas en los datos de los controles. Se declaran mediante la etiqueta dentro del control correspondiente.En el ejemplo se hace uso de un validador con identificador Email, que supuestamente comprueba que el control contiene una dirección de correo electrónico correcta
<h:inputText>
            <f:validator validatorId="Email"/>
    </h:inputText>

Validadores por defecto en JSF

JavaServer Faces provee una buena cantidad de validadores estándares y ofreciéndole un mecanismo sencillo para implementar sus validadores. Así mismo, según la implementación que utilicemos podemos disponer validadores adicionales a los proporcionados por JSF. JavaServer Faces se basa en mecanismos que le permiten llevar a cabo las siguientes validaciones:

  • Chequear la longitud de una cadena de caracteres
  • Chequear límites para un valor numérico (por ejemplo, >0 o <=100)
  • Chequear que un valor ha sido proporcionado

A continuación se ofrece una tabla resumen con los validadores estándar de jsf

CLASE DE VALIDADORTAG JSFATRIBUTOSDESCRIPCIÓN
DoubleRangeValidatorvalidateDoubleRangeminimum,maximumValida que el valor del componente sea mayor que un mínimo y mayor que un máximo. Este valor debe ser tipo float
LengthValidatorvalidateLengthminimum,maximumValida que la longitud del valor del componente esté entre un mínimo y un máximo
LongRangeValidatorvalidateLongRangeminimum,maximumValida que el valor del componente sea mayor que un mínimo y mayor que un máximo. Este valor debe ser tipo entero

Validando longitudes de cadenas de caracteres y rangos numéricos

Es muy fácil usar validadores JSF dentro de páginas JSF. Simplemente añada etiquetas validadoras al cuerpo de un componente etiqueta, algo tal que así:

<h:inputText value="#{cuestionario.nombre}">
   <f:validateLength maximum="12"/>
</h:inputText>

Este fragmento de código, añade un validador al campo de texto de entrada; cuando el texto del campo de entrada es aceptado, el validador se asegura de que el texto introducido tiene como máximo 12 caracteres. Cuando el validador falla (en este caso, cuando el número de caracteres es mayor de 12), genera un mensaje de error asociado con el componente responsable. Estos mensajes de error pueden ser mostrados en una página JSF por las etiquetas h:message o h:messages.

Validando límites para un valor numérico

Se usan par comprobar el rango de un dato introducido. Por ejemplo:

<h:inputText value="#{regalo.cantidad}">
   <f:validateLongRange minimum="10" maximum="10000"/>
</h:inputText>

El validador chequea que el valor proporcionado es >= 10 y <=10000.

Chequeando valores requeridos

Para chequear si un valor es aportado, no necesita anidar un validador dentro de la etiqueta de entrada. En su lugar, utilice el atributo, required=”true”:

<h:inputText value="#{informe.fecha}" required="true"/>

Todas las etiquetas JSF de entrada, soportan el atributo required. Puede combinar un atributo required con un validador anidado:

<h:inputText value="#{informe.telefono}" required="true"/>
  <f:validateLength minimum="9"/>
</h:inputText>

Comunicación de errores en la validación

JSF, en su estándar, define la existencia de un sistema centralizado de gestión de mensajes, una cola, en donde se irán depositando los mensajes que se generen durante las diferentes fases del ciclo de vida JSF. Este sistema de mensajes admite diferentes niveles de gravedad en los mensajes, de menor a mayor: SEVERITY_INFO, SEVERITY_WARN, SEVERITY_ERROR y SEVERITY_FATAL. Estos niveles son fijados por el código subyacente, bien de la implementación JSF, bien de la propia aplicación, en el momento de creación del mensaje, mediante los métodos de la clase FacesMessage. Un mensaje contiene, además de su marca de gravedad, un resumen y un detalle, datos estos que se pueden tanto indicar en el momento de la creación del mensaje (según el constructor que se use) o bien mediante los apropiados get/set.

La cola de mensajes es accesible a través de los siguientes métodos del objeto FacesContext correspondiente:

  • public void addMessage(String clientId, FacesMessage message);
  • public Interator getClientIdsWithMessages();
  • public Severity getMaximumSeverity();
  • public Iterator getMessages(String clientId);
  • public Iterator getMessages();

Estos métodos acceden directamente a la cola de mensajes, con la idea de que la aplicación acumule mensajes en su proceso para el usuario, tanto en la funcionalidad que aporte la implementación, como en la específica de la aplicación. Sin embargo, en el caso de los validadores el sistema se realiza de una forma diferente, sin acceder a este API: se debe crear un mensaje y lanzarlo en forma de excepción para detener el ciclo JSF en el caso de encontrar un error en la validación de un control.

En cuanto al sistema para devolver todos estos mensajes al usuario, la especificación indica dos controles, UIMessage y UIMessages, que disponen de sus etiquetas en la librería html de JSF: <h:message> y <h:messages>.

  • <h:message>: se asocia a un control determinado mediante su propiedad for, donde se debe poner el identificador del control. Este control admite clases y estilos CSS específicos para cada nivel de gravedad de los mensajes que tenga que mostrar. Mostrará exclusivamente los mensajes de error que haya generado el proceso de los datos del control al que está asociado.
  • <h:messages>: panel para mensajes globales. Aquí se mostrarán tanto los mensajes de proceso de cada control como los mensajes generales que la aplicación haya depositado en la cola de mensajes. También acepta diferentes estilos CSS para modificar su aspecto según la gravedad de los mensajes que se muestren, da la posibilidad de mostrar exclusivamente los mensajes globales (propiedad globalOnly) y definir la disposición de los mensajes (propiedad layout, que puede ser “table” o “list”).

Como crear un validador a medida

En muchas ocasiones resulta interesante crear un validador necesario para un control (especialmente si vamos a tener controles similares y queremos reutilizar la comprobación). Para ello podemos seguir el siguiente proceso:

  • Implementar el interfaz javax.faces.validator.Validator. Este interfaz incluye un método validate que será llamado por el gestor del ciclo JSF en la fase de “aplicación de validadores”. El interfaz de llamada de este método es:
public void validate(    FacesContext context,
     UIComponent component,
     Object value)
     throws ValidatorException

Recibe el contexto JSF en el que es llamado, el objeto en el servidor que representa al control y el valor que el usuario ha introducido. En el caso de que su validación resulte no válida, debe crear un mensaje FacesMessage, encapsularlo en una ValidatorException y lanzarla:

..
 throw new ValidatorException(new FacesMessage(ResumenDelMensaje));
 ...
  • Incluir una constante en su implementación para identificación en el sistema JSF. Este identificador lo usará el sistema JSF para referirse al validador en los archivos de configuración o en los controles que se inserten en las páginas jsf.
public final String VALIDATOR_ID = "IDENTIFICADOR DEL VALIDADOR";
  • En el caso de que el validador necesite parámetros, la clase debe implementar una interfaz más: javax.faces.component.StateHolder. Este interfaz permite almacenar los parámetros del validador entre llamadas.
  • Adicionalmente, la clase debe disponer de un constructor sin parámetros.

Ejemplos

Ejemplo de validador a medida

Se va a comprobar que el valor de un componente se corresponde un número de NIF válido. El algoritmo que se utiliza para validar el NIF es el siguiente

  1. Comprobar que el valor a validar tiene una longitud igual a 9. Los primeros 8 caracteres deben ser números (corresponden al DNI) y el último debe ser una letra (la del NIF)
  2. Almacenar la siguiente lista de letras en una variable: “TRWAGMYFPDXBNJZSQVHLCKE”
  3. Calcular el módulo entero de 23.
  4. Recuperar de la lista de letras la que se encuentra en la posición resultado de efectuar el módulo entero de 23

Creamos el Validador

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

import org.apache.commons.lang.StringUtils;

 

/**
 * Validador de NIF
 * @author Ejemplo
 */

public class NifValidator implements Validator

{

            /**
             * Efectúa el proceso de validación
             */
 public void validate(FacesContext contex,UIComponent component, Object value) throws ValidatorException

            {
              // Si el valor es null, lo transformamos en un valor vacío
                        String valor = StringUtils.defaultString((String)value);
              // el valor debe tener 9 posiciones, de las cuales las primeras deben ser dígitos y la última letra
                        valor=valor.toUpperCase();
                        Pattern mask =  Pattern.compile("[0-9]{8,8}[A-Z]");
                        Matcher matcher = mask.matcher(valor);
                        if(!matcher.matches())
                                    throw new ValidatorException(new FacesMessage("El componente " + component.getId() + " no contiene un NIF válido. Las 8 primeras posiciones deben ser numéricas"));        

                        String dni=valor.substring(0,8);
                        String digitoControl = valor.substring(8,9);
                        // Calculamos la letra de control
                        String letras = "TRWAGMYFPDXBNJZSQVHLCKE";
                        int posicion_modulo = Integer.parseInt(dni)%23;
                        String digitoControlCalculado = letras.substring(posicion_modulo,posicion_modulo+1);
                        if(!digitoControl.equalsIgnoreCase(digitoControlCalculado))
                                   throw new ValidatorException(new FacesMessage("El componente " + component.getId() + " no contiene un NIF válido"));                  
            }
}

Registramos el nuevo Validador en el contexto de JSF

Para ello, editamos el fichero descriptor WEB-INF/validaciones-config.xml e insertamos las siguientes líneas:


<validator>

            <validator-id>ejemplo.nifValidator</validator-id>
                        <validator-class>
                 com.ejemplo.tutorialValidacion.NifValidator
            </validator-class>
</validator>

Creamos la página

La llamaremos validacion_ejemplo.jsp y colgará de WebContent; su contenido es el siguiente:

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://myfaces.apache.org/extensions" prefix="t" %>

<html>
    <head>
                        <title>EJEMPLO VALIDACIONES</title>
                        <link rel="stylesheet" type="text/css" href="/servicios/madeja/css/estilos.css">
            </head>
            <body>
            <f:view>
    <h:form id="idUsuarios" name="gestionUsuariosBean">
    <h:messages id="messageList" styleClass="error" showSummary="true" showDetail="true" />
        <h:panelGrid columns="2" styleClass="gestionUsuariosFormTable"
                         headerClass="gestionUsuariosFormHeader"
                         footerClass="gestionUsuariosFormFooter"
                         columnClasses="gestionUsuariosFormLabels, gestionUsuariosFormInputs" width="600">
        <!-- Nombre -->
        <h:outputLabel for="login" value="Nombre"/>
        <h:panelGroup>                
              <h:inputText id="nombre" styleClass="CajasTexto" size="30" maxlength="100"
                                value="#{gestionUsuariosBean.nombre}">        
                       <f:validateLength maximum="50" />                      
                                     </h:inputText>                               
        </h:panelGroup>          
        <!-- Nif -->       
        <h:outputLabel for="nif" value="Nif"/> 
        <h:panelGroup>   
         <h:inputText id="elNif" styleClass="CajasTexto" size="30" maxlength="10"
                               value="#{gestionUsuariosBean.nif}" required="true">
                    <f:validator validatorId="ejemplo.nifValidator"/>
         </h:inputText>
        </h:panelGroup>
         <h:panelGroup>
            <h:commandButton action="resultado_validacion_nuestra" value="Validar" />
            <f:verbatim> </f:verbatim>
          </h:panelGroup>
                        </h:panelGrid>                        
            </h:form>
            </f:view>
</body>
</html>

Añadimos en WEB-INF/validaciones-config.xml la navegabilidad para el botón Validar:


<navigation-rule>
              <from-view-id>/validacion_ejemplo.jsp</from-view-id>
              <navigation-case>
               <from-outcome>resultado_validacion_nuestra</from-outcome>
               <to-view-id>/resultado_validacion_nuestra.jsp</to-view-id>
               <redirect/>
              </navigation-case>
</navigation-rule>

Y creamos la página de resultados WebContent/resultado_validacion_nuestra.jsp:

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://myfaces.apache.org/extensions" prefix="t" %>
<html>
    <head>
                        <title>Ejemplo VALIDACIONES</title>
                        <link rel="stylesheet" type="text/css" href="/servicios/madeja/css/estilos.css">
            </head>
            <body>
            <f:view>
    <h:form id="idUsuarios" name="gestionUsuariosBean">
    <h:messages id="messageList" styleClass="error" showSummary="true" showDetail="true" />
        <h:panelGrid columns="2" styleClass="gestionUsuariosFormTable"
                         headerClass="gestionUsuariosFormHeader"
                         footerClass="gestionUsuariosFormFooter"
                         columnClasses="gestionUsuariosFormLabels, gestionUsuariosFormInputs" width="600">
        <!-- Nombre -->
        <h:outputLabel for="login" value="Nombre"/>: 
        <h:panelGroup>                
              <h:outputText value="#{gestionUsuariosBean.nombre}"/>         
        </h:panelGroup>          
        <!-- Nif -->       
        <h:outputLabel for="nif" value="Nif"/> 
        <h:panelGroup>   
         <h:outputText value="#{gestionUsuariosBean.nif}"/>
        </h:panelGroup>
        <h:panelGroup>
            <h:commandButton action="home" value="Volver" />
            <f:verbatim> </f:verbatim>
          </h:panelGroup>
                        </h:panelGrid>                        
            </h:form>
            </f:view>
</body>
</html>

Enlaces externos