Fluent Validation en C# - La potente y sencilla herramienta de validación de datos
La validación de datos es uno de los pilares del desarrollo de software fiable y, en las aplicaciones .NET modernas, se necesita una forma sólida, mantenible y escalable de gestionarla. Aquí es donde entra FluentValidation, una popular biblioteca .NET para crear reglas de validación fuertemente tipadas.
En su tutorial de vídeo en profundidad "Fluent Validation in C# - The Powerful Yet Easy Data Validation Tool", Tim Corey lleva a los espectadores a través del proceso de uso de FluentValidation paso a paso. En este artículo, seguiremos el recorrido de Tim, resumiendo puntos clave y ejemplos de código mientras integramos conceptos relevantes como validadores personalizados, validadores encadenados, integración ASP.NET y compatibilidad con tiempos de ejecución más antiguos como .NET Core 3.1 y .NET Standard 2.0.
Introducción: ¿Por qué FluentValidation?
Tim abre el vídeo explicando cómo la validación de datos se convierte a menudo en algo repetitivo y desordenado. Por ejemplo, copiar y pegar reglas de validación similares en distintas partes de un proyecto viola el principio DRY (Don't Repeat Yourself). En su lugar, presenta FluentValidation, una biblioteca de validación .NET que es gratuita, potente y funciona incluso en modelos que no se poseen, lo que la hace ideal para proyectos comerciales.
Tim hace hincapié en la importancia de practicar lo que se aprende y señala a los espectadores su serie Desafío semanal para desarrollar habilidades.
Descripción general de la aplicación demo
Tim utiliza una aplicación de demostración WinForms en la que un usuario puede introducir:
-
Nombre de pila
-
Apellidos
-
Balance de cuentas
- Fecha de nacimiento
Aunque se trata de una demostración de interfaz de usuario, los principios de validación se aplican igualmente bien a ASP.NET Core, pruebas de API e incluso aplicaciones de consola.
El peligro de confiar en las entradas del usuario
En este punto, Tim recuerda a los desarrolladores: "Nunca te fíes del usuario" A menudo, las entradas pueden ser impredecibles, como escribir "ten" para la edad en lugar de 10. Es esencial validar esa entrada antes de guardarla en la base de datos.
Esboza ejemplos de reglas de validación:
-
Los nombres y apellidos no deben estar vacíos.
-
El balance de la cuenta debe seguir las normas financieras, como un requisito mínimo para el patrocinio financiero.
- La fecha de nacimiento no debe ser futura ni tener más de 120 años.
¿Dónde debe ir la lógica de validación?
Tim explora las opciones de configuración de los validadores:
-
Dentro del formulario de interfaz de usuario
-
En la clase modelo utilizando anotaciones de datos
- En una clase de validación independiente con FluentValidation
Señala que las anotaciones de datos son limitadas y a menudo no son adecuadas cuando se trabaja con bibliotecas externas o cuando se necesita una lógica de validación más personalizada.
Instalación de FluentValidation
Utilizando Visual Studio, Tim añade FluentValidation a su proyecto a través de NuGet. Instala la versión 8.1.0 pero señala que FluentValidation es multiplataforma y compatible con:
-
.NET Standard 2.0
-
.NET Core
-
ASP.NET
-
WPF
-
Xamarin
- Más información
La configuración de Tim también funciona para aquellos que necesitan compatibilidad con tiempos de ejecución más antiguos, incluido FluentValidation 11, que es compatible con .NET Core 3.1 y versiones anteriores.
Creación de una clase validadora
Tim demuestra la creación de reglas de validación fuertemente tipadas mediante la creación de una nueva clase:
public class PersonValidator : AbstractValidator<Person>
public class PersonValidator : AbstractValidator<Person>
Esta clase contiene toda la lógica de validación del modelo Persona. Utilizando la interfaz fluida, las reglas de validación se definen dentro del constructor.
Primera regla de validación: Nombre
Tim escribe una regla utilizando una expresión lambda:
RuleFor(p => p.FirstName).NotEmpty();
RuleFor(p => p.FirstName).NotEmpty();
Utiliza un validador var para validar el objeto Persona:
var validator = new PersonValidator();
ValidationResult results = validator.Validate(person);
var validator = new PersonValidator();
ValidationResult results = validator.Validate(person);
A continuación, realiza un bucle a través de cualquier fallo de validación para mostrar mensajes fáciles de usar en un cuadro de lista.
Longitud de cadena y mensajes personalizados
Tim amplía la regla de validación:
RuleFor(p => p.FirstName)
.NotEmpty().WithMessage("First name is empty")
.Length(2, 50).WithMessage("Length of first name is invalid");
RuleFor(p => p.FirstName)
.NotEmpty().WithMessage("First name is empty")
.Length(2, 50).WithMessage("Length of first name is invalid");
Mediante el uso de validadores encadenados, esta regla garantiza que el nombre no esté vacío ni sea demasiado corto o largo. Tim introduce Cascade(CascadeMode.Stop) para detener la validación al primer fallo.
Validación personalizada: Caracteres válidos en los nombres
Tim implementa un validador personalizado utilizando un método llamado:
private bool BeAValidName(string name)
private bool BeAValidName(string name)
Se eliminan los espacios y guiones y se garantiza que la cadena solo contenga letras Unicode, lo que permite la compatibilidad con caracteres internacionales.
La regla personalizada se aplica así
.Must(BeAValidName).WithMessage("{PropertyName} contains invalid characters");
.Must(BeAValidName).WithMessage("{PropertyName} contains invalid characters");
Esta estructura de método es perfecta para adaptarla a otros campos, como una función lógica de validación de códigos postales personalizada:
private bool BeAValidPostcode(string postcode)
{
// Add custom logic here to specify a valid postcode format
}
private bool BeAValidPostcode(string postcode)
{
// Add custom logic here to specify a valid postcode format
}
A continuación, se podría utilizar en un validador como:
RuleFor(c => c.Postcode).Must(BeAValidPostcode)
.WithMessage("Please specify a valid postcode");
RuleFor(c => c.Postcode).Must(BeAValidPostcode)
.WithMessage("Please specify a valid postcode");
Esto es habitual en proyectos comerciales que requieren la clase pública CustomerValidator u otros validadores específicos del dominio.
Uso de variables incorporadas en mensajes de error
Tim muestra cómo mejorar dinámicamente los mensajes con marcadores de posición como:
-
{NombreDeLaPropiedad}
-
{Longitud total}
- {LongitudMínima} y {LongitudMáxima}
Esto se traduce en mensajes de error contextuales como
"Length of First Name is invalid (was 105)"
"Length of First Name is invalid (was 105)"
Esto facilita a los usuarios la corrección de errores de introducción.
Apellido y localización
Tim copia la lógica de validación de FirstName para LastName, gracias al formato reutilizable con {PropertyName}. También menciona WithLocalizedMessage() para ASP.NET o aplicaciones globales que necesiten soporte multilingüe.
Importante: CascadeMode es una regla específica
Tim aclara que CascadeMode.Stop se aplica a reglas individuales, no globalmente en todo el modelo. Si tanto FirstName como LastName están vacíos, ambas reglas se activarán, incluso si CascadeMode está activado.
Validación de fecha de nacimiento
A continuación, Tim añade una regla para garantizar que la fecha de nacimiento sea realista:
private bool BeAValidAge(DateTime dob)
{
var currentYear = DateTime.Now.Year;
var dobYear = dob.Year;
return dobYear <= currentYear && dobYear > (currentYear - 120);
}
private bool BeAValidAge(DateTime dob)
{
var currentYear = DateTime.Now.Year;
var dobYear = dob.Year;
return dobYear <= currentYear && dobYear > (currentYear - 120);
}
Usado así:
RuleFor(p => p.DateOfBirth)
.Must(BeAValidAge)
.WithMessage("Invalid {PropertyName}");
RuleFor(p => p.DateOfBirth)
.Must(BeAValidAge)
.WithMessage("Invalid {PropertyName}");
Este es un buen patrón para validar datos temporales como fechas o ventanas de caducidad.
Pensamientos finales y recomendaciones
Tim concluye resumiendo las principales ventajas de FluentValidation:
-
Lógica de validación centralizada
-
Validadores personalizados fáciles de crear
-
Compatible tanto con .NET 5 como con tiempos de ejecución más recientes y antiguos
-
Admite modelos complejos, listas y reglas asíncronas
- Ideal tanto para aficionados como para proyectos comerciales
Anima a los lectores a explorar la documentación de FluentValidation para usos avanzados, incluyendo reglas anidadas, validación de propiedades de correo electrónico, etc.
Conclusión
FluentValidation permite a los desarrolladores .NET crear reglas de validación fuertemente tipadas que son reutilizables, expresivas y mantenibles. Tanto si desarrollas con .NET Core como con .NET 8 o mantienes sistemas heredados en .NET Core 3.1, esta biblioteca facilita la validación de datos.
Con funciones como:
-
La interfaz fluida para crear reglas
-
Compatibilidad con la lógica de validación de códigos postales personalizada
-
Fácil integración con Visual Studio
-
Compatibilidad con pruebas de API, WinForms y ASP.NET
- Tratamiento robusto de los fallos de validación
FluentValidation es una herramienta imprescindible for .NET. Para más información, vea el vídeo completo vídeo y suscríbase al canal de Tim Channel para ver más vídeos sobre C#.
Consejo: Si es la primera vez que utiliza FluentValidation, pruebe a implementar su propio CustomerValidator con reglas para propiedades como public string Name, string Postcode, etc. Pruébalo con una API simulada o un formulario de interfaz de usuario para obtener experiencia práctica.
