Validating Phone Numbers Effectively with ASP.NET Core 3.1 Razor Pages
Time to read: 10 minutes
Data validation is an essential part of application design and development, and telephone numbers are as tricky to validate as they are ubiquitous. In many cases a phone number will be the primary way your organization communicates with its customers. Whether the communication will be by voice, SMS, or messaging app, having a correct phone number is a requirement.
Developers using .NET Core and the .NET Framework can do validation for a number of different data types, including phone numbers, with the System.ComponentModel.DataAnnotations namespace, but the PhoneAttribute class has its limitations. To learn more, see the .NET Data Validation section of the previous Twilio Blog post on this subject: Validating phone numbers effectively with C# and the .NET frameworks.
Fortunately, the libphonenumber-csharp open source library provides extensive resources for validating and manipulating phone numbers of all types and it’s conveniently available as a NuGet package. This post shows how you can implement libphonenumber-csharp in your APS.NET Core Razor Pages projects and easily leverage capabilities of the library.
To get a better understanding of the complexities of phone number validation, it’s well worth reading Google’s Falsehoods Programmers Believe About Phone Numbers, a distinguished member of the series of “falsehoods programmers believe” articles about names, dates, time, and other aspects of the data universe.
Understanding the case study project
The case study project for this post uses a single ASP.NET Core 3.1 Razor Page to collect a phone number from a user and return information about the phone number to the user. The Razor Page is backed by a model class to hold the phone number entered by the user and information about it. The server-side code uses phonenumberlib-csharp to validate the phone number and provide information about it. It’s a simple demo, not production code, but it includes a number of handy techniques you can use in production.
There is a companion repository available on GitHub under an open source license.
Prerequisites
- .NET Core 3.1.1 SDK (includes the Runtime and the .NET Core CLI)
- Visual Studio 2019 version 16.4+, Visual Studio Code, or a code editor of your choice
- Git
Initialize the .NET solution and the C# Razor Pages project
You can get everything set up for this tutorial by executing a series of command-line instructions. These instructions will work for the usual Windows, macOS, and Linux shells, and they’ll create a .NET solution that’s compatible with Visual Studio 2019, Visual Studio Code, and text editors.
Alternatively, you can use your IDE UI to create the solution, project, and components identified in the following steps.
Create a solution directory
Open a command prompt window in the directory in which you’d like to create the directory structure for this solution. Create a new directory called BlipPhoneRazor and make it the active directory:
Initialize a Git repository for the solution
In the BlipPhoneRazor directory, initialize a Git repository:
Add a .gitignore file. If you’re using VS 2019 or VS Code you’ll want to add the standard .gitignore for Visual Studio solutions. The file can be found at:
https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
If you’re using the new open-source PowerShell Core on Windows 10, macOS, or Linux, you can get the remote file with the following Invoke-WebRequest command:
Alternatively, you can get the file with curl or another command line tool, or just copy the contents to a new .gitignore file in the BlipPhoneRazor solution directory.
Add a project reference to the solution file
Execute the following .NET Core CLI commands to create the solution file and add a Razor project to the solution file:
Note that the second command above configures the solution file, but it doesn’t create the project directory or project files; it just adds a listing for a C# project in the solution file.
Create the BlipPhoneRazor project in its own subdirectory using the ASP.NET Core 3.1 Razor Pages template by executing the following CLI commands in the BlipPhoneRazor solution directory:
Once you see the “Restore succeeded.” message, check the contents of the directory to ensure you see a BlipPhoneRazor.csproj
file along with a few other files and directories.
Add the libphonenumber-csharp NuGet package to the Razor Pages project
Add the libphonenumber-csharp dependency by executing the following .NET Core CLI command in the BlipPhoneRazor project directory:
Create project components
You can add new components to a .NET project from the command line.
To add a new Razor page to the project, execute the following .NET Core CLI command in the BlipPhoneRazor project directory:
Create a new directory, Models, under the project directory and create a new file, PhoneNumberCheck.cs, for a C# class in the Models directory. For PowerShell users, the command to create the class file is:
Note the casing for the new files: getting this right will ensure the object names conform to the convention for namespaces and classes. Also note that C# is the default language for new class libraries, but it pays to be specific.
Run the app to validate the configuration
You can check to be sure what you’ve created thus far compiles and runs with a .NET Core CLI command from the BlipRazorPhone directory:
Go to https://localhost:5001 with your browser. You should see the new, minimalist default “Welcome” page for ASP.NET Core Razor Pages projects with the project name on the left side of the banner at the top.
Go to https://localhost:5001/PhoneCheck. There’s no content on this page yet, so you should only see the banner.
That’s it for the setup! Now would be a good time to check in your code. If you’re still working from the command line in the BlipPhoneRazor project directory(not the parent BlipPhoneRazor solution directory), execute the following command-line instructions:
You should see your project files being added to your Git repo.
Code the app
There are three project components for you to code:
PhoneNumberCheck.cs
– the data model to hold the phone number data,
PhoneCheck.cshtml.cs
– the Razor Page Model that mechanizes the page, and
PhoneCheck.cshtml
– the Razor Page markup for the page content.
The following sections provide the code and explanations of some of the important features of each file, along with links to more information.
Create the data model
Open the PhoneNumberCheck.cs file in the BlipPhoneRazor\Models directory and replace the contents, if any, with the following C# code:
The first two properties, CountryCodeSelected
and PhoneNumberRaw
are used to store information about the phone number provided by the user. The remainder of the properties are used to hold information returned by libphonenumber-csharp.
The model uses .NET Core 3.1 Data Annotations to provide additional information about the data in each property, including the field title to use in user interfaces. This feature enables you to provide information about the data once and have it reflected automatically in places where it’s used, an especially helpful feature when you make changes.
The maximum length of a phone number is a highly contextual thing and depends, in part, on whether your application will allow Phonewords, numbers that enable reaching a specific extension after the connection is made, E.164 formatting, or other characteristics.
Create the Razor PageModel
For developers used to the MVC and MVVM design paradigms, an ASP.NET Core Razor PageModel combines some of the features of a data model, a view model, and a controller. In conjunction with a Razor Page, the
Open the PhoneCheck.cshtml.cs file in the BlipPhoneRazor\Pages directory and replace the existing contents with the following C# code:
The following code snippets illustrate particular features of note in this file.
This using
directive includes the libphonenumber-csharp package. The actual assembly name for the library is PhoneNumbers.dll, hence the name.
The PhoneNumberCheck
data model is bound to the page model using the [BindProperty]
attribute. It’s also configured to bind the model to support HTTP GET requests so it can be used to populate data entry fields with default values. This is a particularly useful technique when you want to include dropdown lists on your data entry form.
The constructor for PhoneCheckModel
creates a new instance of PhoneNumberUtil
with a method instead of the more conventional C# new
keyword because the source project for libphonenumber-csharp is Google’s libphonenumber, which is a Java, C++, and JavaScript library. That’s how they roll.
To convert the raw phone number to a PhoneNumber
number object, parse the raw number using a specific country code. The validity of a phone number can only be established in conjunction with a country code unless the phone number is presented in E.164 format. (For a more concise explanation, see E.164.)
To return the same Razor page to the browser with new or updated data the ModelState
has to be updated. The ModelState
must be updated for input values to be returned to the user interface as well as for the new data derived from the PhoneNumberUtil
object.
Date elements in the ModelState
object are key-value pairs which can be located with the.FirstOrDefault
method. This is preferable to using the .SetModelValue
method in most cases because .SetModelValue
will create a new ModelState
record that won’t be bound to an <input>
field if the value used to locate the key is different than the key name.
The ModelState
keys can be matched to the names of the PhoneNumberCheck
model properties using the C# nameof
operator, which will return the unqualified name of the property. However, the name of the key uses the qualified name of the property, which includes the class name. Because there is no variant of nameof
that returns qualified names, the value to use in searching for the matching key must be constructed from an interpolated string that combines the name of the PhoneNumberCheck
class with the name of the desired property, as follows:
The PhoneNumberUtil
object with throw specific exceptions when the phone number and country code passed to it as arguments exceed specific boundaries. These errors can be trapped and added to the ModelState
object as model errors so the Razor page can display them automatically when it’s redisplayed.
Note that this catch
block won’t trap all errors, so your production code is likely to need to account for other types of errors.
Create the Razor Page
It all begins with the @page
directive.
Open the PhoneCheck.cshtml file in the Pages directory and replace the existing contents with the following Razor markup:
Note that the asp-validation-summary
attribute of the form is set to ModelOnly
. This prevents the model validation rules from being invoked when the form loads because the PhoneNumberCheck
data model bound to the PageModel
is set to support HTTP GET requests.
Also note that fully qualified names like PhoneNumberCheck.CountryCodeSelected
are used to refer to the data model properties bound to form fields. Including the class name is necessary whenever a separate class is bound to the PageModel
object. If you were using properties created directly in the PhoneCheckModel
class, a technique that isn’t very reusable, you’d use just the property name.
At this point you’ve completed all the coding, so look for linter issues that might reveal typos. When you’re ready, check in your code.
Note for Visual Studio Code users: If you’ve installed project dependencies, like libphonenumber-csharp, from the command line the OmniSharp debugger can get confused and tell you it can’t find a namespace in a using
statement. This can usually be resolved by closing and reopening VS Code.
Run and test the application
If you are using Visual Studio 2019 you can, of course, press F5 to get things rolling. Visual Studio Code users will need to create a launch profile first. There’s an example in the companion repository if you need one. From the command line, execute dotnet run
and go to the indicated URL in a browser.
Go to the new PhoneCheck page at: https://localhost:<port>/PhoneCheck.
You should see that Issuing Country is prepopulated with “US”.
Enter “800-flowers” in Phone Number and click/tap Check.
You should see results indicating that “800-flowers” is a valid, toll-free number in the US and that it converts to the digits “800 356-9377”, as illustrated in the screenshot below.
Converting phonewords to digits is a handy feature of libphonenumber-csharp. [It’s also worth noting that this author has been very satisfied with 1-800-flowers.com.]
The library includes a number of other features beyond the five demonstrated here, so experiment with more of the properties and methods of PhoneNumberUtil
and the PhoneNumber
object.
Sample Phone Numbers
Here are some numbers that will demonstrate different aspects of the library:
Issuing Country |
ISO Code |
Number |
Notes |
Vatican City |
VA |
+39 06 69812345 |
E.164 format |
Switzerland |
CH |
446681800 |
International from US |
United States |
US |
617-229-1234 x1234 |
Extension |
United States |
US |
212-439-12345678901 |
Too long (>16 digits) |
Possible enhancements
There are a number of ways you can expand on the functionality provided in the case study app:
- Convert the phone number validation page to a shared component so it can be reused.
- Add a dropdown list so users can select the Issuing Country from a canonical ISO standard list.
- Use the JavaScript version of libphonenumber to perform validation on the client side before sending data to the web server.
- Add a data persistence layer so you can store validated phone numbers.
- Use Twilio Verify to validate phone numbers more comprehensively and determine their capabilities. Verify handles variables you don’t see, like carrier regulations and device-specific capabilities, Verify spots and solves for mission critical communication variables, ensuring your message is always delivered.
Summary
This post explains why validating phone numbers is important and demonstrates how you can do it effectively in ASP.NET Core 3.1 Razor Pages projects with the libphonenumber-csharp open source library. It also shows you how to bind a separate data model class to a Razor PageModel, use the data model to prepopulate data entry fields, and how to return data to the same HTML page used for data entry. It also shows how to use the ModelState
object to return errors from the libphonenumber-csharp PhoneNumberUtil
object to the user interface using the built-in capabilities of Razor Pages.
Additional resources
Here are some references to more background information and additional learning:
Introduction to Razor Pages in ASP.NET Core – If you’re new to Razor Pages this is the place to start.
What's new in ASP.NET Core 3.1 – Go here if you need to get caught up on the latest features. Be sure to check the What’s new entry for v3.0 as well, since most of the significant new features were released in 3.0.
TwilioQuest – An action-adventure game for learning programming. How cool is that?
AJ Saulsberry is a Technical Editor at Twilio. Reach out to him if you’ve been “there and back again” with a .NET development topic and want to get paid to write about it for the Twilio blog.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.