# Thursday, March 11, 2010

I love ASP.NET MVC 2 validations for client and server via annotations. Steve Sanderson's xVal is great too, but in this post I want to focus on a custom validation for MVC 2 which is frustratingly missing from the out-of-the-box validations. There is a very nice StringLengthAttribute which allows you to specify a maximum length but does not provide for a minimum length.

At first I attacked this deficiency with a regex like this:

[RegularExpression("[\\S]{6,}", ErrorMessage = "Must be at least 6 characters.")]
public string Password { get; set; }

This approach just seems clunky. Happily, while reading Scott Allen's piece on MVC 2 validation in MSDN magazine, I came across a critical reference to one of Phil Haack's blog posts which I must have overlooked.

Thanks go to Phil's easy to follow instructions on writing a custom validator for MVC 2 that will work on both client and server sides.

So here's my custom MinStringLengthAttribute and supporting code which let's me do something easier and cleaner like this:

[StringLength(128, ErrorMessage = "Must be under 128 characters.")]
[MinStringLength(3, ErrorMessage = "Must be at least 3 characters.")]
public string PasswordAnswer { get; set; }

If you really wanted to get creative, you could produce a combination of the two, but I'll leave that for another day.

Here's the code for the attribute and the required validator:

public class MinStringLengthAttribute : ValidationAttribute
{
  public int MinLength { get; set; }

  public MinStringLengthAttribute(int minLength)
  {
    MinLength = minLength;
  }

  public override bool IsValid(object value)
  {
    if (null == value) return true;     //not a required validator
    var len = value.ToString().Length;
    if (len < MinLength)
      return false;
    else
      return true;
  }
}

public class MinStringLengthValidator : DataAnnotationsModelValidator<MinStringLengthAttribute>
{
  int minLength;
  string message;

  public MinStringLengthValidator(ModelMetadata metadata, ControllerContext context, MinStringLengthAttribute attribute)
    : base(metadata, context, attribute)
  {
    minLength = attribute.MinLength;
    message = attribute.ErrorMessage;
  }

  public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
  {
    var rule = new ModelClientValidationRule
    {
      ErrorMessage = message,
      ValidationType = "minlen"
    };
    rule.ValidationParameters.Add("min", minLength);
    return new[] { rule };
  }
}

Here's the code in the Global.asax.cs that registers the validator:

protected void Application_Start()
{
  RegisterRoutes(RouteTable.Routes);
  DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MinStringLengthAttribute), typeof(MinStringLengthValidator));
}

Here's the javascript to hook up the client side:

Sys.Mvc.ValidatorRegistry.validators["minlen"] = function(rule) {
  //initialization
  var minLen = rule.ValidationParameters["min"];

  //return validator function
  return function(value, context) {
    if (value.length < minLen) 
      return rule.ErrorMessage;
    else
      return true;  /* success */
  };
};

Now, in your view, add <% Html.EnableClientValidation(); %> and that's all there is to it.

Friday, March 12, 2010 6:35:24 PM (Mountain Standard Time, UTC-07:00)
I'm sure you probably already know this but for MinStringLength you can also do:

[StringLength(int.MaxValue, MinimumLength=3, ErrorMessage="Must be at least 3 characters long")]
Michael
Friday, March 12, 2010 10:57:31 PM (Mountain Standard Time, UTC-07:00)
@Michael, yes thanks for pointing that out. I should have been more specific. The MinimumLength property is not there in version 3.5 of System.ComponentModel.DataAnnotations.StringLengthAttribute but thankfully it is in 4.0. So if you're still using VS 2008 and .NET 3.5 SP1, this attribute example might be helpful. Hopefully the pattern is helpful for other validation needs as well.
Comments are closed.