Proyecto de Integracion
 

Validación de datos con el plugin Validator

El uso del método validate del ActionForm requiere escribir código Java. No obstante, muchas validaciones pertenecen a uno de varios tipos comunes: dato requerido, dato numérico, verificación de longitud, verificación de formato de fecha, etc. Struts dispone de un paquete opcional llamado Validator que permite efectuar distintas validaciones típicas de manera automática, sin escribir código. Validator tiene las siguientes características principales:

Configuración

Antes que nada, necesitaremos incluir en nuestro proyecto el archivo .jar con la implementación de validator (commons-validator-nº_version.jar).

Validator es un plugin de struts. Los plugins que se van a usar en una aplicación deben colocarse en la etiqueta <plugin> del struts-config.xml. Esta etiqueta se coloca al final del fichero, inmediatamente antes de la etiqueta de cierre de </struts-config>

  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
   <set-property property="pathnames"
    value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>

El atributo value de la propiedad pathnames indica dónde están y cómo se llaman los dos ficheros de configuración de Validator, cuya sintaxis veremos en el siguiente apartado. Podemos tomar el validator-rules.xml que viene por defecto con la distribución de Struts, mientras que el validation.xml lo escribiremos nosotros.

Definir qué validar y cómo

La definición de qué hay que validar y cómo efectuar la validación se hace, como ya se ha visto, en dos ficheros de configuración separados:

  • validator-rules.xml: en el que se definen los validadores. Un validador comprobará que un dato cumple ciertas condiciones. Por ejemplo, podemos tener un validador que compruebe fechas y otro que compruebe números de DNI con la letra del NIF. Ya hay bastantes validadores predefinidos, aunque si el usuario lo necesita podría definir los suyos propios (sería este el caso de validar el NIF). En la mayor parte de casos podemos usar el fichero que viene por defecto con la distribución de Struts. Definir nuevos validadores queda fuera del ámbito de estos apuntes introductorios.
  • validation.xml: en él se asocian las propiedades de los actionForm a alguno o varios de los validadores definidos en el fichero anterior. Las posibilidades de Validator se ven mejor con un fichero de ejemplo que abordando todas las etiquetas XML una por una:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE form-validation PUBLIC
   "-//Apache Software Foundation//DTD Commons Validator 
   Rules Configuration 1.0//EN"
   "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">
<form-validation>
  <formset>
    <form name="registro">
      <field property="nombre" depends="required,minlength">
         <var>
            <var-name>minlength</var-name>
            <var-value>6</var-value>
         </var>
      </field>
      ...
    </form>
  </formset>
  ...
</form-validation>

Simplificando, un <formset> es un conjunto de ActionForms a validar. Cada ActionForm viene representado por la etiqueta <form>. Dentro de él, cada propiedad del bean se especifica con <field>. Cada propiedad tiene su nombre (atributo property) y una lista de los validadores que debe cumplir (atributo depends). Algunos validadores tienen parámetros, por ejemplo el validador minlength requiere especificar la longitud mínima deseada. Los parámetros se pasan con la etiqueta <var>, dentro de la cual <var-name> es el nombre del parámetro y <var-value> su valor.

Resumiendo, en el ejemplo anterior estamos diciendo que el campo "nombre" debe contener algún valor no vacío (required) y que su longitud mínima (minLength) debe ser de 6 caracteres

Los validadores estándar

Validator incluye 14 validadores básicos ya implementados, los cuales se muestran en la tabla siguiente

Validador Significado Parámetros
required dato requerido y no vacío (no todo blancos) ninguno
mask emparejar con una expresión regular mask: e.r. a cumplir. Se usan las librerías de Jakarta RegExp.
intRange,longRange,floatRange,doubleRange valor dentro de un rango min, max. Estos validadores dependen respectivamente de int,long,... por tanto en el depends de por ejemplo un intRange debe aparecer también int.
maxLength máxima longitud para un dato maxLength
minLength longitud mínima para un dato minLength
byte,short, integer, long, double, float dato válido si se puede convertir al tipo especificado ninguno
date verificar fecha datePattern, formato de fecha especificado como lo hace java.text.SimpleDateFormat. Si se utiliza el parámetro datePatternStrict se verifica el formato de modo estricto. Por ejemplo, si el formato especifica dos días para el mes, hay que poner 08, no valdría con 8, que sí valdría con datePattern
creditCard verificar número de tarjeta de crédito ninguno
email verificar direccion de e-mail ninguno
url verificar URL Varios. Consultar documentación de Struts

Por ejemplo, supongamos que tenemos un campo en el que el usuario debe escribir un porcentaje, permitiendo decimales. Podríamos usar el siguiente validador:


<field property="porcentaje" depends="required,double,doubleRange">
        <var><var-name>min</var-name><var-value>100</var-value></var>
        <var><var-name>max</var-name><var-value>0</var-value></var>
</field>

Obsérvese que doubleRange usa también double y por eso lo hemos tenido que incluir en el depends.

Mensajes de error

¿Dónde están definidos realmente los mensajes de error?

Lo normal será mostrar algún mensaje de error si algún validador detecta que los datos no son correctos. Para ello podemos usar las etiquetas de Struts <html:messages> o <html:errors> que vimos en sesiones anteriores. Recordemos que dichas etiquetas asumen que el error está en un fichero .properties almacenado bajo una determinada clave. Validator supone automáticamente que la clave asociada a cada validador es "errors.nombre_del_validador". Por tanto, para personalizar los mensajes del ejemplo anterior, deberemos incluir en el fichero .properties algo como:

errors.required=campo vacío
errors.minLength=no se ha cumplido la longitud mínima

Por razones históricas,el mensaje de error asociado al validador mask no se busca bajo la clave errors.mask, sino errors.invalid. Nótese que si no definimos estas claves e intentamos mostrar el error, Struts nos dirá que no se encuentra el mensaje de error (es decir, Struts no define mensajes de error por defecto). No obstante, si no nos gusta el nombre de la clave por defecto, podemos cambiarlo mediante la etiqueta <msg>

<form name="registro">
  <field property="nombre" depends="required,minlength">
  <msg name="required" key="campo.nombre.noexiste"/>
     <var>
         <var-name>minlength</var-name>
         <var-value>6</var-value>
     </var>
  </field>
  ...
</form>

Y en el fichero .properties, tendríamos:

campo.nombre.noexiste=el nombre no puede estar vacío

Con lo cual personalizamos el mensaje, pudiendo poner mensajes distintos en caso de que el campo vacío sea otro. En el siguiente apartado veremos una forma mejor de personalizar los mensajes: pasarles parámetros.

Mensajes de error con parámetros

Es mucho más intuitivo para el usuario personalizar el mensaje de error, adaptándolo al error concreto que se ha producido. En el ejemplo anterior, es mucho mejor un mensaje "el campo nombre está vacío" que simplemente "campo vacío". Podemos usar las etiquetas <arg0>...<arg4> para pasar parámetros a los mensajes de error.

<form name="registro">
  <field property="nombre" depends="required,minlength">
  <arg0 name="required" key="nombre" resource="false"/>
     <var>
         <var-name>minlength</var-name>
         <var-value>6</var-value>
     </var>
  </field>
  ...
</form>

El atributo name indica que este argumento solo se usará para el mensaje de error del validador required. Si el atributo no está presente, el argumento se usa para todos (Es decir, en este caso, también para minlength). En principio, el atributo key es el valor del argumento, aunque ahora lo discutiremos con más profundidad, junto con el significado del atributo resource.

Ahora en el fichero .properties debemos reservar un lugar para colocar el argumento 0. Como ya vimos en la primera sesión, esto se hace poniendo el número del argumento entre llaves:

errors.required=campo {0} vacío
...

Por tanto, el mensaje final que se mostrará a través de <html:messages> o <html:errors> será "campo nombre vacío". En el ejemplo anterior, el parámetro resource="false" de la etiqueta <arg0> se usa para indicarle a validator que el valor de key debe tomarse como un literal. En caso de ponerlo a false (y también por defecto) el valor de key se toma como una clave en el fichero .properties, es decir, que nombre no se tomaría literalmente sino que en el .properties debería aparecer algo como:

nombre=nombre

Donde la parte de la izquierda es la clave y la de la derecha el valor literal. Nótese que en este ejemplo ambos son el mismo valor pero nada nos impide por ejemplo poner nombre=nombre de usuario o cualquier otra cosa. A primera vista, este nivel de indirección en los argumentos de los mensajes puede parecer absurdo, pero nótese que si se tradujera la aplicación a otro idioma solo necesitaríamos un nuevo .properties, mientras que de la otra forma también habría que modificar el validation.xml. Veremos más sobre internacionalización de aplicaciones en la siguiente sesión.

Con algunos validadores es útil mostrarle al usuario los parámetros del propio validador, por ejemplo sería útil indicarle que el nombre debe tener como mínimo 6 caracteres (sin tener que poner literalmente el "6" como parte del mensaje, por supuesto). Esto se puede hacer usando el lenguaje de expresiones (EL) en el parámetro key del argumento:

<form name="registro">
  <field property="nombre" depends="required,minlength">
  <arg0 key="nombre" resource="false"/>
  <arg1 name="minlength" key="${var:minlength}" resource="false"/>
     <var>
         <var-name>minlength</var-name>
         <var-value>6</var-value>
     </var>
  </field>
  ...
</form>

Y modificamos el .properties para que quede:

errors.required=campo {0} requerido
errors.minlength={0} debe tener una longitud mínima de {1} caracteres

Modificar el ActionForm para Validator

Si deseamos usar Validator, en lugar de ActionForm lo que debemos utilizar es una clase llamada ValidatorForm. Así nuestra clase extenderá dicha clase, teniendo un encabezado similar al siguiente

public class LoginForm extends org.apache.struts.validator.action.ValidatorForm { 

Cuando se utiliza un ValidatorForm ya no es necesario implementar el método validate. Cuando debería dispararse este método, entra en acción Validator, verificando que se cumplen los validadores especificados en el fichero XML.

Validación en el cliente

Se puede generar código JavaScript para validar los errores de manera automática en el cliente. Esto se haría con un código similar al siguiente:

<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
...
<html:form action="/login.do" 
           onsubmit="return validateLoginForm(this)">
...
</html:form>

<html:javascript formName="loginForm"/>

El JavaScript encargado de la validación de un Form se genera con la etiqueta <html:javascript>, especificando en el atributo formName el nombre del Form a validar. En el evento de envío del formulario HTML (onsubmit) hay que poner "return validateXXX(this)" donde XXX es el nombre del Form a validar. Validator genera la función JavaScript validateXXX de manera que devuelva false si hay algún error de validación. De este modo el formulario no se enviará. Hay que destacar que aunque se pase con éxito la validación de JavaScript, Validator sigue efectuando la validación en el lado del servidor, para evitar problemas de seguridad o problemas en la ejecución del JavaScript.