MSDN Documentation

Custom Validation in Blazor

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.

Understanding Blazor's Validation Foundation

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.

Implementing Custom Validation Attributes

The most idiomatic way to create reusable custom validation rules is by defining your own validation attributes that inherit from ValidationAttribute.

Creating a Custom Attribute

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);
            }
        }
        

Applying the Custom Attribute

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; }
        }
        

Integrating with Blazor Components

Use the EditForm component along with the model decorated with your custom attribute.

Example Usage: User Profile Form

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!"); } }

Custom Validation with Complex Rules

For more complex validation scenarios that might involve checking multiple fields or external data, you can implement the IValidatableObject interface.

Implementing 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;
            }
        }
        

Using IValidatableObject in Blazor

When 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.

Note: Ensure your model class implementing IValidatableObject is correctly referenced within the EditForm. Blazor's DataAnnotationsValidator will trigger the Validate method automatically.

Client-Side vs. Server-Side Validation

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:

  1. Send the form data to a server endpoint.
  2. Perform validation on the server using the same model and business logic.
  3. Return validation errors to the client if the submission is invalid.

Best Practices for Custom Validation

Conclusion

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.