Creación de una anotación de validación personalizada para Bean Validator.

0
20184

Creación de una anotación de validación personalizada para Bean Validator.

0. Índice de contenidos.


1. Introducción

En este tutorial vamos a mostrar como crear nuestras propias anotaciones de validación para Bean Validator (JSR 303).

Bean Validator define un modelo de metadatos, a través de anotaciones por defecto, y un API para llevar a cabo la validación de nuestros JavaBean. La implementación de referencia es Hibernate Validator.

La especificación no está relacionada con una capa o módulo de aplicación concreta o un modelo de programación, si bien, nosotros lo usamos con el soporte de JSF2 para llevar
a cabo la validación de nuestras clases de negocio
, dentro del ciclo de vida de las peticiones de usuario, en la capa de presentación. JSF tiene su propio sistema de validaciones, aunque también se integra con el sistema de validación de Bean Validator, de modo que la definición de las constraints las mantenemos siempre en el modelo de negocio.

Bean Validator provee de una serie de constraints, si bien, podemos vernos en la necesidad de crear las nuestras propias, en este tutorial vamos a ver cómo hacerlo y, para
ello, vamos a suponer que necesitamos un validador de número nif, con el dígito de control.


2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2.4 GHz Intel Core i7, 8GB DDR3 SDRAM).
  • Sistema Operativo: Mac OS X Lion 10.7.2
  • Hibernate Validator 4.2.0
  • JSF 2.1, Mojarra 2.1.4


3. Creación de una anotación propia.

El primer paso para disponer de nuestro propio validador para Bean Validator es crear una anotación propia, para ello podemos diponer de una interfaz como la que sigue:


import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;

@Target({ METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = NifValidator.class)
@Documented
public @interface Nif {

	String message() default "{com.autentia.core.persistentce.constraints.nif}";

	Class<?>[] groups() default {};
	
	Class<? extends Payload>[] payload() default {};
	
}

Para disponer de nuestras propias anotaciones solo debemos establecer una interfaz precedida de una anotación.

A destacar:

  • @Constraint: declara la clase que implementará la validación,
  • es obligatorio un atributo groups que permite a la especificación agrupar validaciones. Debe ser por defecto un array vacío de Class<?>.
  • el atributo playLoad también es obligatorio para que los clientes del api puedan inicializar la validación con sus propios objetos,
  • otros métodos y propiedades que definamos en la interfaz serán los que admita nuestra anotación de validación, en nuestro caso no necesitamos parámetros,
  • con la propiedad message definimos la clave de internacionalización del mensaje por defecto de nuestro validador.


4. Implementar un validador.

Una vez tenemos nuestra anotación, necesitamos implementar el validador de la misma. Para ello debemos crear una clase que implemente la interfaz ConstraintValidator:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class NifValidator implements ConstraintValidator<Nif, String> {

	private Pattern mask =  Pattern.compile("[0-9]{8,8}[A-Z]");
	
	@Override
	public void initialize(Nif constraintAnnotation) {
	}

	@Override
	public boolean isValid(String value, ConstraintValidatorContext context) {

        final Matcher matcher = mask.matcher(value);

        if(!matcher.matches()){
        	return false;
        }
        
        final String dni = value.substring(0,8);
        final String control = value.substring(8,9);
        final String letters = "TRWAGMYFPDXBNJZSQVHLCKE";
        final int position = Integer.parseInt(dni)%23;

        final String controlCalculated = letters.substring(position,position+1);

        if(!control.equalsIgnoreCase(controlCalculated)){
        	return false;
        }
        return true;
	}
}

En el método initialize podríamos recoger parámetros de la anotación para trabajar con ellos.

El método que realiza la validación es el método isValid que recibe en el primer parámetro el valor a validar, nos vendrá tipado en función de RawType que indiquemos en la
implementación de la interfaz.

A diferencia de los validadores de JSF, aquí no se lanza una excepción en caso de error, simplemente se devuelve un boolean.


5. Internacionalizar el mensaje de error.

Si en la anotación definíamos la clave de internacionalización del mensaje de error por defecto, debemos conocer en qué fichero ubicar la misma.

Todos los mensajes personalizados de Bean Validator se deben ubicar en un fichero llamado ValidationMessages.properties que ubicaremos en el classpath de nuestro módulo core.
En función del idioma a internacionalizar el fichero tendrá el sufijo con los locales soportados (ValidationMessages_en.peroperties, ValidationMessages_fr.peroperties, … )

En nuestro caso, la clave a incluir en dichos ficheros de propiedades es la siguiente:

com.autentia.core.persistentce.constraints.nif = Formato de Nif incorrecto.


6. Haciendo uso de nuestra anotación.

Ahora, en cualquier clase de negocio podemos hacer uso de la anotación @Nif en sus propiedades, como sigue:

@Entity
@Table(name="usuarios")
public class Usuario {
	
	@Id
	@Nif
	private String nif;	
	
	... 
}

Si tenemos la capa de presentación con JSF2, cuando se produzca un error de validación en el campo que muestra dicha propiedad, dentro del ciclo de vida de JSF, se mostrará nuestro mensaje internacionalizado asociado al campo mapeado contra dicha propiedad:



7. Referencias.


8. Conclusiones.

Si definimos únicamente a nivel de modelo nuestras validaciones nos beneficiaremos de su reutilización y, sobre todo, cumpliremos el principio DRY, Don´t Repeat Yourself.

Si, además, tenemos el soporte de Bean Validator en la vista, como podría ser con JSF2, enganchado con su ciclo de vida, mostrar mensajes de validación basados en metadatos es
muy simple.

Un saludo.

Jose

jmsanchez@autentia.com

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