Customize a validation attribute working both client side and server side in asp.net MVC

In asp.net MVC the model is using for holding the data and for validation. There are a lot build in validation for you to use, how? just simply by put “using System.ComponentModel.DataAnnotations;” on top of the your model file then you could like this

using System.ComponentModel.DataAnnotations;

namespace youprojectName.Model
{
    public class Account
    {

       [StringLength(26)]//this is the validate
        public string BusinessName { get; set; }

        ...
       }
}

Those build in validation is very useful and they are both client side and server side validation.

But in real projects we need more validation to suit for our business logic. So in that scenario there is a way if your boss happen just the validation is server side,then simply let your model inherit from :IValidatableObject

then you could override the IEnumerable Validate(ValidationContext validationContext) Member.

Sample like this

       
        //those validation only for server side
        public IEnumerable Validate(ValidationContext validationContext)
        {
            if (Name.ToLower().StartsWith("xxxx"))
            {
                yield return new ValidationResult("Sorry, xxxx, you do not have permisstion to do this");
            }
             .....
        }

But this is not the way supports to write your validation, in MVC validation is a attribute just put on top of your class property.
So how to achieve that?
We could create a custom remote attribute and override IsValid() method.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;

namespace CustomizeValidation.Validation
{
    public class RemoteClientServerAttribute : RemoteAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // Get the controller using reflection
            Type controller = Assembly.GetExecutingAssembly().GetTypes()
                .FirstOrDefault(type => type.Name.ToLower() == string.Format("{0}Controller",
                    this.RouteData["controller"].ToString()).ToLower());
            if (controller != null)
            {
                // Get the action method that has validation logic
                MethodInfo action = controller.GetMethods()
                    .FirstOrDefault(method => method.Name.ToLower() ==
                        this.RouteData["action"].ToString().ToLower());
                if (action != null)
                {
                    // Create an instance of the controller class
                    object instance = Activator.CreateInstance(controller);
                    // Invoke the action method that has validation logic
                    object response = action.Invoke(instance, new object[] { value });
                    if (response is JsonResult)
                    {
                        object jsonData = ((JsonResult)response).Data;
                        if (jsonData is bool)
                        {
                            return (bool)jsonData ? ValidationResult.Success :
                                new ValidationResult(this.ErrorMessage);
                        }
                    }
                }
            }

           
            return new ValidationResult(base.ErrorMessageString);
        }
        //below is several over load for your to use   
        public RemoteClientServerAttribute(string routeName)
            : base(routeName)
        {
        }

        public RemoteClientServerAttribute(string action, string controller)
            : base(action, controller)
        {
        }

        public RemoteClientServerAttribute(string action, string controller,
            string areaName)
            : base(action, controller, areaName)
        {
        }
    }
}

then you could simply use it like this:

public class Customer
    {
       ....
        [StringLength(100)]
        [RemoteClientServer("IsValid", "Account",
        ErrorMessage="Email already in use")] 
//"IsValid" is the method should return Json data at your "Account" controller
        public string Email { get; set; }
       ...
   }

Here is one of solution for write customize both server side and client side validation,but I have another solution also could working in client and server side validation(for those solution is write your server side validation in the model first, then write some more jQuery to the View), this will another post, when I back from my job.

Have Fun!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s