Treatment of spaces-only input in ASP.NET MVC

I guess every framework has some kind of bizarre "default" behavior which can cause problems and will require thorough research and experimenting to understand what's going on. For ASP.NET MVC, one of the most stunning examples is its treatment of space symbols in input. It's a real pity that to understand how to deal with this (quite common) problem properly, one needs to do a lot of experimenting and gathering bits and pieces of information from different sources. This article is an attempt to collect this information and express it in more or less coherent way.

The problem

The situation is indeed very common. Let's suppose your web site is written using ASP.NET MVC, and you have a non-required text field that requires validation, for example email. If you enter the string consisting only of spaces, then your client-side validation will show it as passing validation even if the actual regular expression doesn't allow such strings!

This behavior is extremely misguiding. First, you always starting to think that's the problem with your regular expression, however it's not. Second, this behavior is different on server and client. On server, RegularExpressionAttribute doesn't actually ignore spaces! For example, let's try to write a RegularExpressionAttribute which doesn't allow space symbols. If we'll write a test on its behavior:

        [Test]
        public void RegexValidationSpaces()
        {
            var att = new RegularExpressionAttribute(@"\S+");
            Assert.IsTrue(att.IsValid(""));
            Assert.IsFalse(att.IsValid("  "));
            Assert.IsFalse(att.IsValid("André Maria"));
            Assert.IsFalse(att.IsValid(" André Maria"));
            Assert.IsTrue(att.IsValid("d'Artagnan"));
        }

it will show that it's perfectly working, and for case of spaces-only string (" ") too: it prohibits such string. However if we'll write in our model

public class MyModel
{
    [RegularExpression(@"\S+")]
    public string MyProperty {get;set;}
}

then the textbox generated for editing MyProperty by ASP.NET MVC from Html.TextBoxFor(m => m.MyProperty) will behave differently in regards of validation. Its client-side validation accepts all-spaces string! However its server-side validation still won't accept it. So you will receive the expected error after postback, but only after postback: it won't work as immediate client-side implementation.

This behavior looks very much like a problem in internal implementation, and this question was raised many times on forums, for example here, here and here. So if you want your site to look polished and coherent, you should deal with this problem in one way or another, because this behavior looks confusing not only for developer, but also for user: why should one validation messages show immediately, and other validation messages - only after pressing a save button?

What is really going on?

Now, let's look in more detail at what's going on in JavaScript. Regex validation is actually a short method in Microsoft's jquery.validate.unobtrusive.js

    $jQval.addMethod("regex", function (value, element, params) {
        var match;
        if (this.optional(element)) {
            return true;
        }
 
        match = new RegExp(params).exec(value);
        return (match && (match.index === 0) && (match[0].length === value.length));
    });

It just registers another validation method for JQuery Validation plugin. In this implementation it follows conventions and recommendations of this plugin. In particular, it calls this.optional(element) exactly as it was done in plugin documentation samples. The purpose of this call is to skip validation if the field is blank and not required. Now, if we'll look at the implementation of this.optional by JQuery Validation, we'll see the following code:

        optional: function(element) {
            return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
        },

It trims the value! OK, maybe we can just change the code for regex validation to avoid this call to optional?

    $jQval.addMethod("regex", function (value, element, params) {
        var match;
        if (element.value.length === 0) {
            return true;
        }
 
        match = new RegExp(params).exec(value);
        return (match && (match.index === 0) && (match[0].length === value.length));
    });

It kinda works. But it fails because of another trim in different place of JQuery Validation code:

        onfocusout: function(element) {
            if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
                this.element(element);
            }
        },

so when leaving text input with spaces-only string, validation still won't be triggered. And looking at JQuery Validation scripts, we can see some other trims there.

So, the actual problem is the default treatment of spaces by JQuery Validation. First, it deals with spaces differently than .Net validation, hence the different behavior on server and client. Second, I don't think this treatment is intuitive, and I am not alone, see this comment on JQuery Validation issue tracker. On the other hand, JQuery Validation plugin is very popular and it's very hard to blame Microsoft for using it as a basis for client-side validation. OK, but what should we do with this problem?

The solution

There are several ways to solve this problem directly. For example, it is possible to fix the situation by writing your own CustomValidator (with ValidateEmptyText property set to true so that it will be able to handle spaces-only strings). Also it can be solved in JavaScript. There's also at least one commercial set of validators which can solve this problem for you. However I wouldn't recommend the aforementioned approaches. The problem of treating spaces-only string is actually much broader, it's more a usability problem, not just a validation problem.

Why should user even think about removing spaces he mistakenly added? Use cases where text field should be filled with spaces intentionally are exceptionally rare. If so, why bother user with manual removing of mistakes in his input if all can be fixed in automated way? As it was said by Francesco Abbruzzese in his excellent answer to this thread, "One should not confuse validation with input normalization. Normalization MUST NOT BE PERFORMED manually by the user..."

So, probably the best solution for this problem is to allow user to enter spaces-only strings, and deal with them server-side by converting them to NULL.  The simplest way to implement such kind of normalization is by using binders. For example, to solve this problem, we are using slightly modified version of this binder:

    public class TrimModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (valueResult == null || string.IsNullOrWhiteSpace(valueResult.AttemptedValue)) return null;
            return valueResult.AttemptedValue;
        }
    }

which requires this registration in global.asax to make it work:

    ModelBinders.Binders.Add(typeof(string), new TrimModelBinder());

It's not a workaround: it addresses a different problem than just validation of spaces-only string, and addresses it effectively. However, the proper fix for this problem is making default implementation of validation work in the same way on server and client, but it would require significant change in JQuery Validation code. Well, I hope someone will implement this task some day. Until then, I'd recommend to use the server-side trimming.

Note: this article is about ASP.NET MVC but 90% of what was said (except binder) should be valid for ASP.NET too.

Comments

Now i'm glad that I noticed

Now i'm glad that I noticed www.yumasoft.com , exactly the suitable information which wanted! wish you luck in New Year!

Hello there, just discovered

Hello there, just discovered www.yumasoft.com on Yahoo, and found that it's really awesome. I'm gonna watch out for brussels. I will appreciate if you keep writing about this subject in future. Lots of people will benefit from your writing. Cheers! wish you all the best in 2014!

Nothing against the article,

Nothing against the article, but I disagree with a couple of points to some extenct. I'm probably a minority though, lol. Thanks for sharing it on www.yumasoft.com . wish you luck in New Year!

Hello www.yumasoft.com Team!

Hello www.yumasoft.com Team! Im new to this web page and I wanted to say Hi. This can be a great web-site and im glad I joined. New to this blog thanks for the welcome. I just came to this wonderful blog and wanted to introduce myself to everyone. This really is such an excellent internet site. wish you all the best in 2014!

I envy your capability to

I envy your capability to publish wonderful article on www.yumasoft.com simply wanted to say I like this ! wish you all the best in 2014!

Nice

Nice read. Thanks.