Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

UrlValidator, a Project Widget used by Revalee

0.00/5 (No votes)
22 May 2014 1  
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:

<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:

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:

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):

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

Property Type Definition Example
AllowAbsolute bool Allows (or prohibits) the use of an absolute Uri http://localhost:46200/Absolute/Path
AllowRelative bool Allows (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:

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:

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