blazor-data-annotations-validation

Validación en Blazor con DataAnnotations

  • 4 min

La validación con DataAnnotations consiste en declarar reglas sobre el modelo mediante atributos estándar de .NET.

En lugar de repetir comprobaciones en cada manejador, decoramos las clases con metadatos que describen qué valores acepta cada propiedad.

Blazor abraza este estándar y lo integra nativamente en sus formularios.

El espacio de nombres System.ComponentModel.DataAnnotations

Lo primero es definir las reglas del juego. Esto se hace en la clase C# que actúa de modelo, usando atributos.

Imaginemos un formulario de registro.

using System.ComponentModel.DataAnnotations;

public class RegistroModel
{
    [Required(ErrorMessage = "El nombre es obligatorio")]
    [StringLength(50, MinimumLength = 3, ErrorMessage = "El nombre debe tener entre 3 y 50 caracteres")]
    public string Nombre { get; set; } = string.Empty;

    [Required]
    [EmailAddress(ErrorMessage = "Formato de email incorrecto")]
    public string Email { get; set; } = string.Empty;

    [Range(18, 99, ErrorMessage = "Debes ser mayor de edad")]
    public int Edad { get; set; }

    [Required]
    [RegularExpression(@"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$",
        ErrorMessage = "La contraseña debe tener 8 caracteres, letras y números")]
    public string Password { get; set; } = string.Empty;
}
Copied!

Fíjate que hemos centralizado la lógica. No importa si usamos esta clase en Blazor, en una API o en una aplicación de consola: las reglas viajan con el dato.

Los atributos más comunes son:

  • :idea[[Required]]: El campo no puede ser null ni estar vacío.
  • :idea[[StringLength]]: Longitud máxima (y opcionalmente mínima) de caracteres.
  • :idea[[Range]]: Para números, define un mínimo y máximo.
  • :idea[[EmailAddress]], [Phone], [Url]: Validaciones de formato predefinidas.
  • :idea[[RegularExpression]]: La navaja suiza para patrones complejos.

Activar la validación con DataAnnotationsValidator

Si pones estos atributos en tu modelo y ejecutas el EditForm que creamos en el artículo anterior, verás que… no pasa nada. Blazor te dejará enviar el formulario vacío.

¿Por qué? Porque EditForm es agnóstico al sistema de validación. No asume que quieras usar DataAnnotations (podrías querer usar FluentValidation, por ejemplo).

Para activar el motor de DataAnnotations, debemos añadir el componente dentro del formulario.

<EditForm Model="@registro" OnValidSubmit="ProcesarRegistro">

    <DataAnnotationsValidator />

</EditForm>
Copied!

Al añadir este componente, Blazor escanea el modelo, lee los atributos y se suscribe a los eventos de cambio de campo. Ahora, si intentas enviar el formulario vacío, OnValidSubmit no se ejecutará.

Mostrando los errores en pantalla

El formulario ya sabe que hay errores, pero el usuario no. Necesitamos decírselo. Blazor nos ofrece dos componentes para esto.

ValidationSummary

Muestra una lista con todos los errores del formulario. Es útil para depurar o para ponerlo en la cabecera de formularios muy grandes.

<EditForm Model="@registro" OnValidSubmit="Guardar">
    <DataAnnotationsValidator />

    <div class="alert alert-danger">
        <ValidationSummary />
    </div>

</EditForm>
Copied!

ValidationMessage

Lo ideal es mostrar el error justo debajo del campo que ha fallado. Para eso usamos ValidationMessage, vinculándolo al campo específico con For.

Necesitamos usar una expresión lambda: For="@(() => registro.Nombre)".

<div class="mb-3">
    <label>Nombre:</label>
    <InputText class="form-control" @bind-Value="registro.Nombre" />

    <ValidationMessage For="@(() => registro.Nombre)" />
</div>
Copied!

Por defecto, este componente renderiza un div con la clase validation-message (que suele tener color rojo).

Ejemplo completo

Vamos a ver cómo queda todo junto. Observa cómo combinamos el EditForm, los InputComponents y la validación.

<h3>Registro de Usuario</h3>

<EditForm Model="@registro" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />

    <div class="mb-3">
        <label class="form-label">Nombre Completo</label>
        <InputText class="form-control" @bind-Value="registro.Nombre" />
        <ValidationMessage For="@(() => registro.Nombre)" />
    </div>

    <div class="mb-3">
        <label class="form-label">Correo Electrónico</label>
        <InputText class="form-control" @bind-Value="registro.Email" />
        <ValidationMessage For="@(() => registro.Email)" />
    </div>

    <div class="mb-3">
        <label class="form-label">Edad</label>
        <InputNumber class="form-control" @bind-Value="registro.Edad" />
        <ValidationMessage For="@(() => registro.Edad)" />
    </div>

    <button type="submit" class="btn btn-primary">Registrar</button>

</EditForm>

@code {
    private RegistroModel registro = new();

    private void HandleValidSubmit()
    {
        // Este método SOLO se ejecuta si todas las reglas [Required], [Range], etc. se cumplen.
        Console.WriteLine("Formulario válido y enviado.");
    }
}
Copied!

Clases CSS de validación

Como mencionamos en el artículo de Inputs, cuando un campo falla la validación, Blazor le añade automáticamente la clase CSS invalid.

Esto nos permite poner el borde rojo sin lógica extra:

/* Blazor añade esta clase automáticamente a los inputs con error */
.form-control.invalid {
    border-color: #dc3545;
    background-image: url("data:image/svg+xml,..."); /* Icono de error */
}

/* Estilo para el texto del mensaje de error */
.validation-message {
    color: #dc3545;
    font-size: 0.875em;
}
Copied!

Personalizar mensajes

Los mensajes por defecto (“The field Name is required”) son funcionales pero feos, y además están en inglés.

Puedes definir el texto mediante la propiedad ErrorMessage de cada atributo:

[Required(ErrorMessage = "¡Oye! Necesito saber tu nombre.")]
public string Nombre { get; set; }
Copied!

La validación del navegador o de un componente interactivo mejora la experiencia, pero no sustituye la validación en el servidor. Cualquier dato recibido desde el cliente debe volver a validarse antes de guardarlo o usarlo en una operación sensible.