C#

Validate User Input With Regular Expressions in Blazor WebAssembly

In this article, we are going to learn how to use regular expressions for user input validation in Blazor WebAssembly applications.

To download the source code for this article, you can visit our GitHub repository.

Let’s get started.

Regular Expressions

Regular expressions (regex or regexp for short) are character sequences that we use to match patterns on strings. Every major programming language has a regex engine embedded in it and C#, of course, is no exception.

When working in a Blazor WebAssembly application, we can easily embed complex validations in our data model using regular expressions in combination with data annotations.

Example Project: Employee Registration Form

To test all our examples we are going to create a simple Blazor WebAssembly client application. It hosts a single page with a hypothetical employee registration form.

We will create a new Blazor WebAssembly project and add an EmployeeRegistration.razor page and a model for our form in EmployeeRegistrationModel.cs :

Basic Model With Data Annotations

Now, let’s add the EmployeeRegistrationModel class. It contains properties matching each of the data fields in our form:

using System.ComponentModel.DataAnnotations;

namespace UserInputValidationWithRegex.Models
{
    public class EmployeeRegistrationModel
    {
        [Required]
        public string Name { get; set; }

        [Required]
        public string Address { get; set; }

        [Required]
        public string ZipCode { get; set; }

        [Required]
        public string SocialSecurityNumber { get; set; }

        [Required]
        public string Email { get; set; }

        [Required]
        public string Username { get; set; }

        [Required]
        public string Password { get; set; }

        [Required]
        public string PhoneNumber { get; set; }
    }
}

First, we include the data annotations namespace. Then, we use the Required attribute to make all fields mandatory. Yes, we will ask our users to fill out our form completely. 

Next, our page component will be based on a standard EditForm component, some input controls, and a couple of other validation related components:

        @page "/"

@using UserInputValidationWithRegex.Models

<PageTitle>@pageTitle</PageTitle>
<h1>@pageTitle</h1>

<EditForm Model="@model" OnValidSubmit="@HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div class="mb-3 col-9">
        <label for="name" class="form-label">Employee Name</label>
        <InputText id="name" class="form-control" @bind-Value="model.Name" aria-describedby="nameHelp" />
        <div id="nameHelp" class="form-text">Employee's first, middle and last names.</div>
    </div>

    <div class="mb-3 col-9">
        <label for="address" class="form-label">Address</label>
        <InputText id="address" class="form-control" @bind-Value="model.Address" aria-describedby="addressHelp" />
        <div id="addressHelp" class="form-text">Employee's address.</div>
    </div>

    <div class="mb-3 col-5">
        <label for="zipCode" class="form-label">Zip Code</label>
        <InputText id="zipCode" class="form-control" @bind-Value="model.ZipCode" aria-describedby="zipCodeHelp" />
        <div id="zipCodeHelp" class="form-text">Zip Code.</div>
    </div>

    <div class="mb-3 col-5">
        <label for="socialSecurityNumber" class="form-label">Social Security Number</label>
        <InputText id="socialSecurityNumber"
                   class="form-control"
                   @bind-Value="model.SocialSecurityNumber"
                   aria-describedby="socialSecurityNumberHelp" />
        <div id="socialSecurityNumberHelp" class="form-text">Employee's social security number.</div>
    </div>

    <div class="mb-3 col-9">
        <label for="email" class="form-label">E-Mail</label>
        <InputText id="email" class="form-control" @bind-Value="model.Email" aria-describedby="emailHelp" />
        <div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
    </div>

    <div class="mb-3 col-9">
        <label for="username" class="form-label">Username</label>
        <InputText id="username" class="form-control" @bind-Value="model.Username" aria-describedby="usernameHelp" />
        <div id="usernameHelp" class="form-text">Choose a unique username.</div>
    </div>

    <div class="mb-3 col-9">
        <label for="password" class="form-label">Password</label>
        <InputText type="password"
                   id="password"
                   class="form-control"
                   @bind-Value="model.Password"
                   aria-describedby="passwordHelp" />
        <div id="passwordHelp" class="form-text">Choose a strong password.</div>
    </div>

    <div class="mb-3 col-9">
        <label for="phone" class="form-label">Phone Number</label>
        <InputText id="phone" class="form-control" @bind-Value="model.PhoneNumber" aria-describedby="phoneHelp" />
        <div id="phoneHelp" class="form-text">Main contact phone number.</div>
    </div>

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

</EditForm>

@code {
    private string pageTitle = "Employee Registration Form";
    private EmployeeRegistrationModel model = new();

    private void HandleValidSubmit()
    {
        // TODO: Handle form data
    }
}

Right after the page directive, we include our Models namespace so this page has access to our EmployeeFormModel class.

The next important element is our EditForm component, whose Model attribute is set to a private field model defined in the code section at the bottom of the page. Also, we will set the form’s OnValidSubmit attribute to the HandleValidInput private method defined in the code section as well.

We must add a ValidationSummary component inside our form so all the validation messages are displayed on the screen. Also, a DataAnnotationsValidator inspects our form’s model object and calls validation against data annotations.

Finally, we add a set of InputText components and a simple submit button. We bind InputText components to each of our model’s properties using the @bind-Value directive.

To learn more about the form validation in Blazor WebAssembly, you can read our article here. Also, if you want to see the custom validation in action, you can read more about that here.

At this point, we should be able to successfully run our project and enter data in our form. If you try to submit the form leaving some fields empty the Required annotations we have already in place should trigger some validation error messages:

Perfect. But that’s not what we’re here for.

Validate Blazor WebAssembly Form Fields With Regex

Now let’s explore how we can use regular expressions to easily implement more complex validations.

Employee Name

We want all employee names in our database to be as authentic as possible. One step we can take towards that is to check that the data input in that field looks like a person’s name.

Considering only the English language, a typical person’s name will be composed of uppercase and lowercase letters, spaces, and symbols like periods ., hyphens - or apostrophes '. On the other hand, a person’s name would never contain numeric characters or symbols like backslashes \ or asterisks *. Finally, we consider that any given name must be a minimum of two characters long.

Let’s come up with a regular expression that checks all of the previous conditions and apply it to our form model using data annotations:

[Required]
[RegularExpression(@"^[a-zA-Z\s.\-']{2,}$", ErrorMessage = "Employee name contains invalid characters.")]
public string Name { get; set; }

Creating regular expressions is a deep topic and we won’t be explaining the basics. For now, we are good knowing that a-z and A-Z both match the ranges of characters that together comprehend all letters in the alphabet regardless of their case. At the same time, \s matches spaces, . periods and ' apostrophes. We must escape the hyphen \- to avoid ambiguities with the range syntax. Finally, {2,} is a quantifier and validates that the total length of the string is equaled to or exceeds two.

That’s it, we have implemented a relatively complex validation logic with a single line of code:

Address

Addresses usually contain a street number and name at the beginning. Following that, we will require to enter the city name and state separated by a period. These are some examples of valid addresses:

23839 Arroyo Park Rd. Dayton, Minnesota(MN)
365 Payson St. San Dimas, California(CA)

A new expression to match all that logic will be significantly more complex. However, its building blocks remain the same:

[Required]
[RegularExpression(@"^[0-9]+\s[a-zA-Z\s\-']{2,}.\s?[a-zA-Z\s\(\),]{2,}$", ErrorMessage = "Wrong address format.")]
public string Address { get; set; }

Here, we use a series of character matchers and quantifiers along with some fixed characters to define the different sections of our address format.

For instance, [0-9]+ will match a street number with one or more digits followed by a fixed space \s. Next, the street name matcher [a-zA-Z\s\-']{2,} accepts at least two letters followed by a period and an optional space .\s? and so on.

Zip Code

Zipcodes in the United States come in two flavors: A basic zip code is just five digits. There are, as well, extended codes adding four extra digits that may or may not be separated from the first five by a hyphen.

According to that, all these would valid zipcodes:

12345
12345-6789
123456789

To build a regular expression that matches exactly our rules for zip codes we will use the alternate token |. We will provide two expressions and get a positive match whenever the input string fits either one or the other: 

[Required]
[RegularExpression(@"^[0-9]{5}$|^[0-9]{5}-?[0-9]{4}$", ErrorMessage = "Invalid zipcode format")]
public string ZipCode { get; set; }

Social Security Number

United States’ social security number format is 000-00-0000. However, not all digits are valid in every position. For instance, 666 or 900 to 999 aren’t valid for the first segment and none of the segments can consist of only zeros.

This time, we use the negative lookahead assertion ?! to ensure that a certain set of expressions do not match the pattern without consuming string positions itself:

[Required]
[RegularExpression(@"^(?!(000|666|9))\d{3}-(?!00)\d{2}-(?!0000)\d{4}$", ErrorMessage = "Invalid SSN")]
public string SocialSecurityNumber { get; set; }

Username and Password

Typically, a username input field would only allow a certain length and a specific subset of characters.

[Required]
[RegularExpression(@"^[\w.\-]{2,18}$", ErrorMessage = "Invalid username.")]
public string Username { get; set; }

Here, we simplify the expression by using a new matcher \w that is equivalent to [a-zA-Z0-9_].

Also, we use a quantifier to limit the length of the string to eight-teen characters. However, it is probably better to use the MaxLength data annotation attribute for that.

Passwords, on the other hand, usually require at least one occurrence of certain character types. We will require our new employee to provide a password that matches the following rules:

  • At least one
    • number (0-9)
    • uppercase letter
    • lowercase letter
    • non-alpha-numeric character
  • Password length is between 8 and 32 characters with no spaces
[Required]
[RegularExpression(@"^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,32}$", 
                    ErrorMessage = "Password doesn't meet security rules.")]
public string Password { get; set; }

Checking that there’s at least one occurrence of a certain type of character is the tricky part. However, we can achieve it using the positive lookahead assertion ?=. It checks that a given pattern appears in a string without consuming string positions itself.

Email

Validating email addresses can be rather complex. Anyway, we can go for something simple that does the job just fine:

[Required]
[RegularExpression(@"^((?!\.)[\w-_.]*[^.])(@\w+)(\.\w+(\.\w+)?[^.\W])$", ErrorMessage = "Invalid email address.")]
public string Email { get; set; }

Phone Number

Finally, let’s validate our employee’s phone number. Phone numbers in the united states consist of ten digits and may be preceded by the country code +1. Additionally, some people like to insert spaces to separate groups of digits +1 123 456 7890:

[Required]
[RegularExpression(@"^([+]?\d{1,2}[-\s]?|)\d{3}[-\s]?\d{3}[-\s]?\d{4}$", ErrorMessage = "Invalid phone number.")]
public string PhoneNumber { get; set; }

Conclusion

In this article, we have learned what regular expressions are and why they are useful for user input validation.

We have learned how to integrate regular expressions in our Blazor WebAssembly application using data annotation attributes and, finally, we have implemented various complex validations in an example form.

Code Maze

Share
Published by
Code Maze

Recent Posts

HttpClient vs RestSharp – Which One to Use in .NET

HttpClient and RestSharp are HTTP Client libraries that we can use to consume APIs. Working…

Updated Date Jul 7, 2022

Testing Repository Pattern Using Entity Framework

Unit Testing is extremely important for creating robust software. It's very simple in principle but…

Updated Date Jul 6, 2022

Shell Sort in C#

Have you ever needed to sort a list of items, but didn't want to use…

Updated Date Jul 5, 2022

How to Resolve Instances With ASP.NET Core DI

In ASP.NET Core dependency injection, we usually register injectable dependencies at the start of our…

Jul 4, 2022

Ranges and Indices in C#

In this article, we are going to learn more about ranges and indices in C#,…

Updated Date Jul 2, 2022

Code Maze Weekly #128

Issue #128 of the Code Maze weekly. Check out what's new this week and enjoy…

Updated Date Jul 1, 2022