Click here to Skip to main content
Click here to Skip to main content

Implementing an ASP.NET Validator - UriValidator

, 22 Jan 2004
Rate this:
Please Sign up or sign in to vote.
An article describing how to implement a custom ASP.NET validator.

Introduction

With ASP.NET, we were introduced to validators. In the most simplistic explanation, validators are used to verify that the information in a given control matches the values you are expecting.

All ASP.NET validators are derived from the abstract BaseValidator class, which provides a single abstract method (EvaluateIsValid) that we will need to implement.

ASP.NET includes several built-in controls, including:

  • RequiredFieldValidator -- Makes the associated input control a required field.
  • CustomValidator -- Performs user-defined validation on an input control.
  • RegularExpressionValidator -- Validates whether the value of an associated input control matches the pattern specified by a regular expression.
  • CompareValidator -- Compares the value entered by the user into an input control with the value entered into another input control or a constant value.
  • RangeValidator -- Checks whether the value of an input control is within a specified range of values.

These validators are very well documented in the MSDN library, and I will not go into the details of these here.

Creating the UriValidator class

For this article, we will walk through the process of creating a simple validator to verify that a value entered into a TextBox control is a valid URI.

The process of accomplishing this is fairly basic. By deriving from the BaseValidator class, we inherit all of the functionality of the ASP.NET validators, and we simply need to provide our own implementation to determine whether or not the input is valid.

To do this, we will declare our class like this:

public class UriValidator : System.Web.UI.WebControls.BaseValidator
{
    protected override bool EvaluateIsValid()
    {
        TextBox textbox = FindControl(
                    this.ControlToValidate) as TextBox;

        if (textbox != null)
        {
            try
            {
                Uri uri = new Uri(textbox.Text);
                return true;
            }
            catch (UriFormatException)
            {
                return false;
            }
        }
        
        return false;            
    }
}

What we are doing here is finding the TextBox control that we want to validate, and then attempt to assign the Text property to a new Uri instance. If the Text property is not a valid Uri, then this throws a UriFormatException, which in turn causes our validator to return false.

Next, the BaseValidator class offers a virtual method called ControlPropertiesValid, which will allow us to validate that the ControlToValidate matches a specific type. In our case, we only want to validate TextBox controls, so we will add the following method to our UriValidator class.

protected override bool ControlPropertiesValid()
{
    Control ctrl = FindControl(
            this.ControlToValidate) as TextBox;
    return ctrl != null;
}

Supporting selected Uri schemes

At this point, we have a fully functional validator, which will support any type of URI that is submitted. However, we may run across the case where we want to limit the scheme of the URI. To accomplish this, let's create a new enum, which will correspond to the available URI schemes.

The code will look something like this:

[
Flags(),
Serializable()
]
public enum UriScheme
{
    File = 0x0001,
    Ftp = 0x0002,
    Gopher = 0x0004,
    Http = 0x0008,
    Https = 0x0010,
    Mailto = 0x0020,
    News = 0x0040,
    Nntp = 0x0080,
    All = UriScheme.File | UriScheme.Ftp | 
        UriScheme.Gopher | UriScheme.Http | UriScheme.Https |
        UriScheme.Mailto | UriScheme.News | UriScheme.Nntp
}

Notice that we have marked our enum with the FlagsAttribute. This is necessary so that we can assign a bitwise mask of values. For example: UriScheme.Http | UriScheme.Https to only allow the Http and Https schemes to validate.

Now that we have our enum set up, we will want to create a property that we can access via code or designer to set the URI schemes that we wish to accept.

Let's add this property accessor to our UriValidator class:

[
Category("Behavior"),
Description("Gets or sets a value indicating which Uri schemes will be accepted."),
DefaultValue("All")
]
public string AcceptedSchemes
{
    get
    {
        object savedState = this.ViewState["AcceptedSchemes"];
        if (savedState != null)
        {
            return ((UriScheme)savedState).ToString();
        }
        return UriScheme.All.ToString();
    }
    set 
    {
        this.ViewState["AcceptedSchemes"] = (UriScheme)Enum.Parse(
            typeof(UriScheme), value, false); 
    }
}

We have specified this property as a string so that the user can enter these into the designer property grid as a comma-delimited list of values (i.e.: Http, Https, Mailto), which still allowing the users to use the enumeration in code (i.e.: AcceptedSchemes = UriScheme.Http | UriScheme.Https | UriScheme.Mailto).

We have also specified that this property lives in viewstate so that the value will be persisted across postbacks.

Tying it all together

Now that we have support for accepted schemes, we will need to modify the EvaluateIsValid method to also validate that a supported scheme is contained in the input value.

To do this, let's slightly modify the method and add a helper method to assist us in validating the scheme.

protected override bool EvaluateIsValid()
{
    TextBox textbox = FindControl(
        this.ControlToValidate) as TextBox;

    if (textbox != null)
    {
        try
        {
            Uri uri = new Uri(textbox.Text);
            return IsValidScheme(uri.Scheme);
        }
        catch (UriFormatException)
        {
            return false;
        }
    }
    
    return false;
}

private bool IsValidScheme(string scheme)
{
    if (this.AcceptedSchemes.IndexOf("All") != -1)
    {
        return true;
    }

    if (scheme ==  Uri.UriSchemeFile)
    {
        return this.AcceptedSchemes.IndexOf("File") != -1;
    }
    else if (scheme == Uri.UriSchemeFtp)
    {
        return this.AcceptedSchemes.IndexOf("Ftp") != -1;
    }
    else if (scheme == Uri.UriSchemeGopher)
    {
        return this.AcceptedSchemes.IndexOf("Gopher") != -1;
    }
    else if (scheme == Uri.UriSchemeHttp)
    {
        return this.AcceptedSchemes.IndexOf("Http") != -1;
    }
    else if (scheme == Uri.UriSchemeHttps)
    {
        return this.AcceptedSchemes.IndexOf("Https") != -1;
    }
    else if (scheme == Uri.UriSchemeMailto)
    {
        return this.AcceptedSchemes.IndexOf("Mailto") != -1;
    }
    else if (scheme == Uri.UriSchemeNews)
    {
        return this.AcceptedSchemes.IndexOf("News") != -1;
    }
    else if (scheme == Uri.UriSchemeNntp)
    {
        return this.AcceptedSchemes.IndexOf("Nntp") != -1;
    }
    else
    {
        return false;
    }
}

With this, we have completed our validator and are ready to drop this onto a webform to make sure everything works.

Using the code

In it's most simplest form, this control can be used like this:

<html>
    <body>
        <form runat="server" ID="Form1">
            <h3>UriValidator</h3>

            <asp:TextBox id="textbox" 
                runat="server"></asp:TextBox>

            <hw:UriValidator id="urlValidator" 
                runat="server" AcceptedSchemes="Http, Https"
                ControlToValidate="textbox" 
                ErrorMessage="The Uri entered is an incorrect format."/>

            <asp:Button id="button" runat="server" 
                Text="Submit"></asp:Button>
        </form>
    </body>
</html>

When you run this project, you can test that this is working by entering http://www.mattberther.com into the textbox. You will see that no validation errors are occurring. However, if you change the textbox value to Foo, you will notice that you get an error, because 'Foo' is not a valid URI.

Now, one final test to validate that our scheme processing is working. Enter ftp://ftp.microsoft.com into the textbox, and click the button. Again, you will see that it fails, because our accepted schemes do not include FTP.

Documentation

This code has been documented using the /// documentation syntax. I prefer to use NDoc, which can be found at SourceForge, as my documentation generator.

Package Contents

In the download, you will find the pre-built component, along with source and an NDoc generated .chm file that you can use for reference when using the component.

Feedback

Your feedback is important to me and I am always willing to make improvements. If you found this article useful, don't forget to vote.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Matt Berther
Web Developer
United States United States
I am a 31 year old software developer from Boise, Idaho. I have been working with computers since approximately age 12, when my 6th grade teacher got me hooked. My parents got me a Commodore 64 for Christmas that year, and it's been downhill ever since. Wink | ;)
 
I have taught myself software development, beginning with Microsoft's Visual Basic 4.0. Approximately 5 years ago, I was given an opportunity to work in the tech field for a company called HealthCast. HealthCast's web-based technology solutions manage and control access to applications and patient information stored in legacy systems, client-server applications, or web solutions.
 
I left HealthCast in February 2003, to pursue a fantastic opportunity with Healthwise. Healthwise provides content to organizations and informs people to help them make better health decisions, creating Prescription-Strength Information™ tools that doctors trust and consumers use.
 
I have been working with the .NET framework since version 1.0, beta 2 and have not looked back since. Currently, I am most intrigued by the uses of the .NET framework and XML to create distributed, reusable applications.

Comments and Discussions

 
GeneralMissing the Register TagPrefix PinmemberPhil G3-Aug-05 2:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 23 Jan 2004
Article Copyright 2004 by Matt Berther
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid