Validadores Integrados en Java
En el ecosistema Java, los paquetes javax.validation.constraints y org.hibernate.validator.constraints proporcionan anotaciones esenciales para validar campos de objetos Java. A continuación, se describen categorías clave y ejemplos de uso.
Antoaciones de javax.validation.constraints
- Validación de nulidad:
@Null(campo debe ser nulo),@NotNull(campo no puede ser nulo),@NotEmpty(colección o cadena no vacía),@NotBlank(cadena sin espacios en blanco y no vacía). - Validación de rango:
@Size(min, max)(longitud de cadena o colección),@Min(value)y@Max(value)(límites numéricos),@DecimalMiny@DecimalMax(valores decimales como cadenas),@Digits(integer, fraction)(dígitos enteros y decimales). - Validación de booleanos:
@AssertTrue(campo debe ser verdadero),@AssertFalse(campo debe ser falso). - Validación de fechas:
@Future(fecha futura),@Past(fecha pasada), y variantes que incluyen el presente. - Otras validaciones:
@Email(formato de correo),@Pattern(regexp)(expresión regular),@URL(dirección web válida, extensión de Hibernate).
Anotaciones de org.hibernate.validator.constraints
Este paquete incluye validaciones extendidas como @Length(min, max) para cadenas, @CreditCardNumber para números de tarjeta, @SafeHtml para prevenir ataques XSS, y @UniqueElements para garantizar elementos únicos en colecciones.
Ejemplo Práctico de Validación en una Clase Java
import javax.validation.constraints.*;
import org.hibernate.validator.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
public class Cliente {
@NotNull(message = "El identificador es obligatorio")
private Long identificador;
@NotBlank(message = "El nombre de usuario no puede estar vacío")
@Size(min = 2, max = 30, message = "El nombre debe tener entre 2 y 30 caracteres")
private String nombreUsuario;
@Min(value = 21, message = "La edad mínima es 21 años")
@Max(value = 99, message = "La edad máxima es 99 años")
private Integer edad;
@DecimalMin(value = "1.00", message = "El saldo no puede ser inferior a 1.00")
@DecimalMax(value = "50000.00", message = "El saldo no puede superar 50000.00")
private BigDecimal saldo;
@FutureOrPresent(message = "La fecha de alta debe ser actual o futura")
private LocalDate fechaAlta;
@Email(message = "Formato de correo inválido")
private String correo;
@Pattern(regexp = "^\\d{9}$", message = "El teléfono debe tener 9 dígitos")
private String telefono;
@Length(min = 8, max = 16, message = "La contraseña debe tener de 8 a 16 caracteres")
private String contrasena;
@UniqueElements(message = "Los intereses no pueden repetirse")
private Collection<String> intereses;
// Métodos de acceso (getters y setters)
}
Integración con Spring Boot
Para habilitar la validación en una aplicación Spring Boot, se requiere la dependencia spring-boot-starter-validation. Luego, se aplica la validación en controladores REST mediante anotaciones.
Configuración en un Controlador
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/clientes")
public class ClienteControlador {
@PostMapping
public String registrarCliente(@Valid @RequestBody Cliente cliente) {
return "Cliente registrado exitosamente";
}
}
Manejo Global de Excepciones
Para capturar errores de validación y devolver mensajes claros, se define un manejador global.
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.LinkedHashMap;
import java.util.Map;
@RestControllerAdvice
public class ManejadorExcepcionesGlobal {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> manejarErroresValidacion(MethodArgumentNotValidException ex) {
Map<String, String> errores = new LinkedHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error -> {
String campo = error.getField();
String mensaje = error.getDefaultMessage();
errores.put(campo, mensaje);
});
return ResponseEntity.badRequest().body(errores);
}
}
Creación de un Validador Personalizado
Para validar que una cadena comience con un prefijo específico, se crea una anotación y su validador asociado.
Definición de la Anotación
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidadorPrefijo.class)
public @interface IniciaCon {
String mensaje() default "La cadena debe iniciar con el prefijo especificado";
Class<?>[] grupos() default {};
Class<? extends Payload>[] consecuencias() default {};
String valorPrefijo() default "";
}
Implementación del Validador
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ValidadorPrefijo implements ConstraintValidator<IniciaCon, String> {
private String prefijoRequerido;
@Override
public void inicializar(IniciaCon anotacion) {
this.prefijoRequerido = anotacion.valorPrefijo();
}
@Override
public boolean esValido(String valor, ConstraintValidatorContext contexto) {
if (valor == null) {
return true; // Permitir nulos según lógica de negocio
}
boolean valido = valor.startsWith(prefijoRequerido);
if (!valido) {
contexto.disableDefaultConstraintViolation();
contexto.buildConstraintViolationWithTemplate(
"Se esperaba que empezara con '" + prefijoRequerido + "', pero se encontró: " + valor
).addConstraintViolation();
}
return valido;
}
}
Uso de Validación por Grupos
Para escenarios donde la validación varía según la operación (ej., crear vs. actualizar), se definen interfaces de grupo.
public interface GrupoCreacion {}
public interface GrupoActualizacion {}
public class Cliente {
@NotBlank(groups = GrupoCreacion.class, message = "El nombre es requerido para creación")
@NotBlank(groups = GrupoActualizacion.class, message = "El nombre es requerido para actualización")
private String nombreUsuario;
@Null(groups = GrupoCreacion.class, message = "El ID debe ser nulo al crear")
@NotNull(groups = GrupoActualizacion.class, message = "El ID es obligatorio al actualizar")
private Long identificador;
}
// En el controlador
@PutMapping("/{id}")
public String actualizarCliente(
@PathVariable Long id,
@Validated(GrupoActualizacion.class) @RequestBody Cliente cliente) {
return "Cliente actualizado";
}
Esta configuración permite una validación flexible y reutilizable en aplicaciones Spring Boot, adaptándose a diferentes requisitos de negocio.