Blazor provides a robust framework for building interactive web UIs with C#. While it offers built-in validation mechanisms, you'll often encounter scenarios requiring custom validation logic to enforce specific business rules or data integrity constraints. This document explores how to implement custom validation effectively in your Blazor applications.
Blazor leverages the System.ComponentModel.DataAnnotations
attributes for declarative validation. Components like EditForm
and InputBase
work together with these attributes to handle validation.
For instance, the RequiredAttribute
, StringLengthAttribute
, and RegularExpressionAttribute
are commonly used. However, these cover a subset of potential validation needs.
The most idiomatic way to create reusable custom validation rules is by defining your own validation attributes that inherit from ValidationAttribute
.
Let's create an attribute that ensures a string does not contain specific forbidden words.
// CustomValidationAttributes.cs
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
public class NoForbiddenWordsAttribute : ValidationAttribute
{
private readonly string[] _forbiddenWords;
public NoForbiddenWordsAttribute(params string[] forbiddenWords)
{
_forbiddenWords = forbiddenWords.Select(w => w.ToLower()).ToArray();
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value is string stringValue && !string.IsNullOrEmpty(stringValue))
{
var lowerCaseValue = stringValue.ToLower();
foreach (var word in _forbiddenWords)
{
if (Regex.IsMatch(lowerCaseValue, $@"\b{Regex.Escape(word)}\b"))
{
return new ValidationResult($"The field cannot contain the forbidden word: '{word}'.", new[] { validationContext.MemberName });
}
}
}
return ValidationResult.Success;
}
// Custom error message override
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString ?? "The {0} field contains forbidden words.", name);
}
}
Now, apply this attribute to a property in your model:
// UserProfile.cs
using System.ComponentModel.DataAnnotations;
public class UserProfile
{
[Required]
public string Username { get; set; }
[Required]
[NoForbiddenWords("badword", "nasty")]
public string Bio { get; set; }
}
Use the EditForm
component along with the model decorated with your custom attribute.
Enter some text for your bio. Try using "badword" or "nasty" to see the custom validation in action.
In your Blazor component code-behind (e.g., UserProfileForm.razor.cs
):
@using System.ComponentModel.DataAnnotations
@inject ValidationService ValidationService
@code {
private UserProfile profile = new UserProfile();
private void HandleValidSubmit()
{
// Process the valid form data
Console.WriteLine("Form is valid!");
}
private void HandleInvalidSubmit()
{
// Handle invalid form submission
Console.WriteLine("Form is invalid!");
}
}
For more complex validation scenarios that might involve checking multiple fields or external data, you can implement the IValidatableObject
interface.
IValidatableObject
Modify your model to implement IValidatableObject
:
public class RegistrationForm : IValidatableObject
{
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string ConfirmPassword { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (!string.IsNullOrEmpty(Password) && !string.IsNullOrEmpty(ConfirmPassword) && Password != ConfirmPassword)
{
results.Add(new ValidationResult("Passwords do not match.", new[] { nameof(ConfirmPassword) }));
}
// Add more complex validation here...
// For example, checking email format with a custom regex not covered by DataAnnotations
if (!string.IsNullOrEmpty(Email) && !Regex.IsMatch(Email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$"))
{
results.Add(new ValidationResult("Please enter a valid email address.", new[] { nameof(Email) }));
}
return results;
}
}
IValidatableObject
in BlazorWhen using IValidatableObject
, you need to ensure that Blazor's validation system is aware of it. The EditForm
component, when combined with DataAnnotationsValidator
, automatically calls the Validate
method if the model implements IValidatableObject
.
IValidatableObject
is correctly referenced within the EditForm
. Blazor's DataAnnotationsValidator
will trigger the Validate
method automatically.
Blazor performs validation on the client-side by default when using DataAnnotationsValidator
. However, it's crucial to always perform server-side validation as well, as client-side validation can be bypassed.
To implement server-side validation, you would typically:
Blazor's flexible validation system, combined with custom validation attributes and the IValidatableObject
interface, allows you to implement sophisticated data validation rules tailored to your application's needs. By following best practices, you can ensure data integrity and a robust user experience.