Skip to main content

Cómo realizar la validación de JavaBeans

Escrito por
feature javabeans validation

1 de mayo de 2023

0 minutos de lectura

JavaBeans Validation (Bean Validation) es un modelo de validación disponible desde la introducción de la plataforma Java Enterprise Edition 6. Ofrece una forma estándar para que distintas aplicaciones Java validen los datos de entrada. En una publicación anterior, hablamos sobre cómo se usan los DTO de Java en aplicaciones modernas para evitar filtraciones de datos accidentales.

En este artículo, crearemos una nueva aplicación Java y validaremos algunas cadenas de ejemplo para mostrar cómo funciona Bean Validation. Pero primero, repasemos qué son los Beans.

Los JavaBeans son objetos Java que contienen propiedades y métodos para almacenar y transferir datos entre distintas partes de una aplicación Java. Los Beans son componentes reutilizables que encapsulan los datos y el comportamiento de una aplicación Java. Son una forma estándar de representar objetos y siguen ciertos patrones de diseño y convenciones.

  • Los JavaBeans contienen propiedades, que son variables de instancia que representan el estado de un objeto. Estas variables deben ser privadas. Para acceder a los datos almacenados en un Bean, se usan métodos getter y setter.

  • Los constructores sin argumentos se usan para crear una instancia de un Bean. Podemos agregar constructores para ofrecer distintas formas de inicializar un Bean.

  • Los JavaBeans son serializables, lo que significa que su estado se puede guardar en un archivo o transmitir a través de una red.

  • Los JavaBeans siguen convenciones de nomenclatura, como usar los métodos getXXX y setXXX para acceder al valor de las propiedades y asignarlo.

  • Los JavaBeans suelen incluir clases BeanInfo, que proporcionan información sobre el bean a las herramientas y los IDE.

A menudo usamos JavaBeans como componentes básicos para organizar y estructurar datos en aplicaciones más grandes. Por lo general, contienen variables de instancia (propiedades) y métodos getter y setter (getXXX y setXXX) para acceder a los datos almacenados en un Bean. También usamos JavaBeans para crear objetos personalizados que podemos reutilizar en distintas partes de una aplicación, lo que facilita el mantenimiento y la actualización del código.

Uso de JavaBeans Validation

Uno de los principales usos de los Beans es validar la entrada del usuario, ya que nos permiten especificar restricciones para sus propiedades, como longitudes mínimas y máximas, caracteres permitidos y campos obligatorios. Luego usamos estas restricciones para validar los datos ingresados por los usuarios y asegurarnos de que cumplan los requisitos especificados antes de procesarlos en la aplicación.

En el siguiente ejemplo, usamos Hibernate Validator en un proyecto de Apache Maven. Hibernate Validator es la implementación de referencia de Bean Validation.

Crear una aplicación Java

Primero, debemos agregar una dependencia para Hibernate Validator en el archivo pom.xml de Maven. Para hacerlo, usa el siguiente código:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.5.Final</version>
</dependency>

Luego, agregamos la API de Bean Validation como dependencia en pom.xml con el siguiente código:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

Ahora, creamos un proyecto Java en un archivo nuevo llamado BeanValidation.java e importamos las bibliotecas necesarias:

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.constraints.Pattern;
import java.util.Set;
import javax.validation.ConstraintViolation;

Luego, creamos un Bean sencillo con una propiedad que vamos a validar usando el siguiente código:

public class ExampleBean {
    @NotNull
    @Size(min=2, max=10)
    private String exampleString;

//Getters
    public String getExampleString() {
        return exampleString;
    }

//Setters
    public void setExampleString(String exampleString) {
        this.exampleString = exampleString;
    }
}

En este ejemplo, usamos las anotaciones @NotNull y @Size para especificar que la propiedad exampleString no puede ser nula y debe tener una longitud mínima de dos y máxima de diez caracteres. Establecemos los métodos getExampleString y setExampleStrin como getters y setters de la propiedad exampleString en la clase ExampleBean.

Cómo funciona Bean Validation

Para usar la clase BeanValidation y validar el Bean, ejecutamos lo siguiente:

public class BeanValidation {
    public static void main(String[] args) {
        ExampleBean bean = new ExampleBean();
        bean.setExampleString("hello");
// Create a Validator instance using the default validation factory      
  ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
// Validate an instance of the ExampleBean class and get a set of constraint violations, if any        
Set<ConstraintViolation<ExampleBean>> violations = validator.validate(bean);
        if (!violations.isEmpty()) {
            System.out.println("Errors:");
            for (ConstraintViolation<ExampleBean> violation : violations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("Validation successful");
        }
    }
}

Como asignamos el valor de cadena hello, pasa la validación. Si asignamos a la propiedad exampleString un valor que no cumple las restricciones especificadas en un Bean, el método de validación devuelve un conjunto de objetos ConstraintViolation que describen los errores.

Para manejar los errores cuando las cadenas no pasan la validación, creamos casos de manejo de errores que verifican si hay infracciones y muestran un mensaje de error si se produce alguna.

Este es el código completo del archivo BeanValidation.java:

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.ConstraintViolation;

public class ExampleBean {
    @NotNull
    @Size(min=2, max=10)
    private String exampleString;

    public String getExampleString() {
        return exampleString;
    }

    public void setExampleString(String exampleString) {
        this.exampleString = exampleString;
    }
}

public class BeanValidation {
    public static void main(String[] args) {
        ExampleBean bean = new ExampleBean();
        bean.setExampleString("hello");

        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        Set<ConstraintViolation<ExampleBean>> violations = validator.validate(bean);

        if (!violations.isEmpty()) {
            System.out.println("Errors:");
            for (ConstraintViolation<ExampleBean> violation : violations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("Validation successful");
        }
    }
}

Este ejemplo devuelve un error si la cadena de entrada tiene menos de dos caracteres, más de diez o es nula. Si la cadena no cumple esas restricciones, muestra un mensaje de error con el requisito que no se cumplió.

Este es un ejemplo de restricciones para validar los datos de inicio de sesión del usuario en los campos de correo electrónico y contraseña:

public class UserBean {
    @NotNull
    @Size(min=5, max=30)
    @Pattern(regexp = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}")
    private String email;

    @NotNull
    @Size(min=8, max=15)
    @Pattern(regexp = "((?=.*[a-z])(?=.*\\d)(?=.*[A-Z])(?=.*[@#$%!]).{8,15})")
    private String password;

}

La clase UserBean contiene dos propiedades: email y password. La propiedad email garantiza que los datos ingresados tengan una longitud mínima de cinco y máxima de treinta caracteres, y que coincidan con el patrón de una dirección de correo electrónico válida.

La propiedad password garantiza que la contraseña no sea nula, tenga una longitud mínima de ocho y máxima de quince caracteres, y contenga al menos una letra minúscula, una mayúscula, un dígito y un carácter especial.

Comprender las restricciones

Las restricciones se definen mediante anotaciones: tipos especiales de metadatos que se agregan a clases, campos y métodos de Java. Podemos aplicar restricciones a las propiedades de una clase Bean en distintos lugares, como directamente a la propiedad, a un método getter de la propiedad o a un parámetro del constructor de la clase.

Una vez definidas las restricciones para un Bean, la validación usa un objeto Validator de la API de Bean Validation. El objeto Validator se encarga de comprobar si una instancia de Bean cumple las restricciones de sus propiedades. Si un Bean infringe alguna restricción, Validator devuelve un conjunto de infracciones que indica qué propiedades no pasaron la validación y por qué.

JavaBeans incluye algunas restricciones integradas disponibles de forma predeterminada como parte de la API de Bean Validation, que podemos usar de inmediato. Entre ellas se encuentran @NotNull, @Size, @Pattern, @Min, @Max, @DecimalMin y @DecimalMax. Para ver la lista completa de restricciones integradas, consulta la documentación de Hibernate Validator.

Las restricciones personalizadas son restricciones que definimos para cumplir requisitos de validación específicos. Las creamos definiendo una anotación personalizada e implementando un ConstraintValidator que defina la lógica de validación.

Crear anotaciones personalizadas

Para crear una restricción personalizada para Bean Validation, debemos:

  • Crear una clase de anotación personalizada y anotarla con @Constraint. Esto indica que la clase anotada es una restricción personalizada.

  • Implementar la lógica de validación en una clase que implemente la interfaz ConstraintValidator. La clase debe tener parámetros con la anotación de restricción personalizada y el tipo de elementos que puede validar.

  • Registrar la restricción personalizada agregando una entrada en el archivo de configuración XML de validación o anotando la clase de restricción personalizada con @Constraint(validatedBy=<Validator class>).

Este es un ejemplo de una restricción personalizada llamada ValidSearchTerm:

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

@Documented
@Constraint(validatedBy = ValidSearchTermValidator.class)
@Target({ FIELD })
@Retention(RUNTIME)
public @interface ValidSearchTerm {

    String message() default "Search term contains invalid characters";

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

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

Luego podemos implementar la lógica de validación que usa expresiones regulares para crear la restricción. En este caso, queremos una cadena de búsqueda alfanumérica común que no tenga caracteres especiales:

public class ValidSearchTermValidator implements ConstraintValidator<ValidSearchTerm, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        return value.matches("[A-Za-z0-9]+");
    }
}

Después, usamos la restricción personalizada en un Bean de la siguiente manera:

public class SearchBean {
    @ValidSearchTerm
    private String searchTerm;

    public String getSearchTerm() {
        return searchTerm;
    }

    public void setSearchTerm(String searchTerm) {
        this.searchTerm = searchTerm;
    }
}

Consideraciones finales sobre Bean Validation

Bean Validation ofrece una forma unificada y coherente de validar datos en aplicaciones Java, lo que facilita escribir y mantener código que garantice un procesamiento seguro de los datos. Podemos usar el modelo de Bean Validation para ahorrar el tiempo y el esfuerzo que, de otro modo, dedicaríamos a escribir lógica de validación personalizada.

Con Bean Validation, podemos especificar restricciones en las propiedades de un JavaBean mediante anotaciones. Estas anotaciones pueden ser integradas o personalizadas y definen los requisitos que deben cumplir los datos ingresados por el usuario para considerarse válidos. Luego, el modelo usa estas restricciones para validar el Bean e identificar errores.

Bean Validation también nos permite manejar errores durante la validación. El framework devuelve una lista de infracciones de restricciones, y podemos usar esta información para proporcionar comentarios específicos al usuario. Esto ayuda a mejorar la experiencia del usuario y permite crear una aplicación más sólida y segura.

En general, Bean Validation es una herramienta esencial para garantizar la calidad y la seguridad de las aplicaciones Java. Si seguimos las prácticas recomendadas para este modelo, podemos asegurarnos de que nuestras aplicaciones estén bien diseñadas, sean fáciles de mantener y seguras.

Empieza con Capture the Flag

Aprende a resolver desafíos de Capture the Flag viendo nuestro taller virtual introductorio a pedido.