Click here to Skip to main content
15,885,944 members
Articles / Web Development / HTML

UrlValidator, a Project Widget used by Revalee

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
22 May 2014MIT4 min read 10.3K   58   4  
Creating and implementing a custom URL validator in a web project.

Introduction

You're setting up your ASP.NET MVC project and you realize that one of the parameters in your web.config file is a URL. Wouldn't it be cool if your application could validate that parameter as a URL directly? Yes, it would and here's how.

Background

When we wrote (and continue to write) Revalee—an open source project used to schedule callbacks for web applications—we needed to write a number of internal tools, or “widgets”, that would support the project. One of these was a UrlValidator. By no means was this validator a core component of the project, but it's inclusion certainly did make our lives just a bit easier during development and deployment.

Disclaimer: Revalee is a free, open source project written by the development team that I am a part of. It is freely available on GitHub and is covered by the MIT License. If you are interested, download it and check it out.

UrlValidator

Let's see why we might want such a widget. Suppose, for example, that your web.config includes a custom configuration section, like:

XML
<revalee>
    <clientSettings serviceBaseUri="http://localhost:46200" authorizationKey="YOUR_SECRET_KEY" />
    <recurringTasks callbackBaseUri="http://localhost:64646">
        <task periodicity="daily" hour="00" minute="00" url="/RevaleeTest/DailyRecurring" />
        <task periodicity="hourly" minute="30" url="/RevaleeTest/HourlyRecurring" />
    </recurringTasks>
</revalee>

Now let's focus on the <clientSettings> element, specifically the serviceBaseUri attribute. That's it: no more, no less.

Why should we care about validating (or not) one measly little attribute, you ask? Well, consider the following: as an application installed outside of your control, you have no way of predicting who is going to enter what as a value for the serviceBaseUri attribute. So getting this detail right now, means that it won't come back and bite you in... er, haunt you later.

(To be perfectly clear, the web.config snippet above utilizes the UrlValidator four (4) different times: once for the serviceBaseUri attribute of the <clientSettings> element, once for the callbackBaseUri attribute of the <recurringTasks> element, and once each for url attribute of the two defined <task> elements. I only bring this up to highlight the importance of code reuse, but I digress...)

Web.config

Let's take a look at the serviceBaseUri attribute in the <clientSettings> element again:

XML
serviceBaseUri="http://localhost:46200"

It's clearly meant to be a URL: it's got a scheme, a host, and even, in this example, a port number. We could import this as a string, but why do something in two steps (that is, import it as a string and then convert it to a Uri elsewhere in the code) when you can accomplish the same thing in one step? We want our attribute to validate as a URL when the application loads, more specifically as a Uri. If the attribute's value isn't a valid URL, then our application is misconfigured and can't run. Period.

ConfigurationElement

Since we started with the web.config file, let's work inwards from there so that we'll cover the UrlValidator last. That means we need to define a custom ConfigurationElement, called ClientSettingsConfigurationElement in this example. This class looks like:

C#
using System;
using System.ComponentModel;
using System.Configuration;

namespace Revalee.Client.Configuration
{
    internal class ClientSettingsConfigurationElement : ConfigurationElement
    {
        [ConfigurationProperty("serviceBaseUri", IsKey = false, IsRequired = false)]
        [UrlValidator(AllowAbsolute = true, AllowRelative = false)]
        public Uri ServiceBaseUri
        {
            get
            {
                return (Uri)this["serviceBaseUri"];
            }
        }

        // Other properties omitted for brevity.
        // ...
    }
}

You'll notice two details. First, that the property (that is, ServiceBaseUri) is defined as a Uri (and also, that this["serviceBaseUri"] is cast as Uri). And second, that ServiceBaseUri is marked up with the following custom attribute (more on this in a moment):

C#
[UrlValidator(AllowAbsolute = true, AllowRelative = false)]

So now we see how we could use such an custom attribute. How do we go about defining one? Well...

ConfigurationValidatorAttribute

The custom UrlValidator attribute (shown above) includes two properties: AllowAbsolute and AllowRelative. We'll define these two bool properties now when we create our custom ConfigurationValidatorAttribute.

PropertyTypeDefinitionExample
AllowAbsoluteboolAllows (or prohibits) the use of an absolute Urihttp://localhost:46200/Absolute/Path
AllowRelativeboolAllows (or prohibits) the use of a relative Uri/Relative/Path

Since properties of this custom attribute are both optional (notice that the constructor's signature has no parameters: public UrlValidatorAttribute()), we'll assign each property to have a default value of true. The final code for this new, custom ConfigurationValidatorAttribute looks like this:

C#
using System;
using System.Configuration;

namespace Revalee.Client.Configuration
{
    internal sealed class UrlValidatorAttribute : ConfigurationValidatorAttribute
    {
        private bool _AllowAbsolute = true;

        private bool _AllowRelative = true;

        public UrlValidatorAttribute()
        {
        }

        public bool AllowAbsolute
        {
            get
            {
                return _AllowAbsolute;
            }
            set
            {
                _AllowAbsolute = value;
            }
        }

        public bool AllowRelative
        {
            get
            {
                return _AllowRelative;
            }
            set
            {
                _AllowRelative = value;
            }
        }

        public override ConfigurationValidatorBase ValidatorInstance
        {
            get
            {
                return new UrlValidator(_AllowAbsolute, _AllowRelative);
            }
        }

        // The code for the 'private class UrlValidator' goes here (see below for complete class)
    }
}

Above, we see that the ValidatorInstance property of the ConfigurationValidatorAttribute base class has been overridden and returns a new UrlValidator() of type: ConfigurationValidatorBase. That private class would be defined inline where the comment is located (above). Instead, we'll review that code separately now.

ConfigurationValidatorBase

There are other details defined the UrlValidator, but the Validate() method of the ConfigurationValidatorBase class is where all of the custom validation work happens. Since that's the essence of this endeavor, let's focus on that.

As you may have already guessed, based on the groundwork laid earlier in this article, we are interested in supporting both absolute and relative URLs (or only one or the other). Additionally, in this specific implementation, we are only interested in an absolute Uri that sports either the http or the https scheme; if a Uri is relative, then we don't have to worry about its scheme. So without further ado, the code:

C#
private class UrlValidator : ConfigurationValidatorBase
{
    private bool _AllowAbsolute = true;
    private bool _AllowRelative = true;

    public UrlValidator(bool allowAbsolute, bool allowRelative)
    {
        _AllowAbsolute = allowAbsolute;
        _AllowRelative = allowRelative;
    }

    public override bool CanValidate(Type type)
    {
        return type == typeof(Uri);
    }

    public override void Validate(object value)
    {
        if (value == null)
        {
            return;
        }

        if (value.GetType() != typeof(Uri))
        {
            throw new ArgumentException("The URL attribute is invalid.");
        }

        Uri url = value as Uri;

        if (!_AllowAbsolute && url.IsAbsoluteUri)
        {
            throw new ArgumentException("The URL attribute cannot contain an absolute URL.");
        }

        if (!_AllowRelative && !url.IsAbsoluteUri)
        {
            throw new ArgumentException("The URL attribute cannot contain a relative URL.");
        }

        if (url.IsAbsoluteUri && url.Scheme != Uri.UriSchemeHttp && url.Scheme != Uri.UriSchemeHttps)
        {
            throw new ArgumentException(string.Format("The URL attribute only supports {0} and {1}.",
                                                      Uri.UriSchemeHttp,
                                                      Uri.UriSchemeHttps)
                );
        }
    }
}

...And that's it. A handful of one-line if statements, really the last three in the Validate() method, are what makes this particular UrlValidator tick. Perhaps yours will be more complex.

Conclusion

This article reviewed the specifics of how a UrlValidator might be implemented. In Revalee, usage of this widget made setting up and configuring the project much, much easier. Hopefully, it can do the same for your project.

Further Reading

History

  • [2014.May.22] Initial post.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior) Sage Analytic
United States United States
A member of the Sage Analytic software development studio since 2000. Located in Northern NJ. Likes Formula 1, Star Wars, and other things. Enjoys a good laugh.

Comments and Discussions

 
-- There are no messages in this forum --