Click here to Skip to main content
15,886,060 members
Articles / Programming Languages / Javascript

Extending the MVC3 RemoteAttribute to Validate server-side when JavaScript is Disabled

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
5 Apr 2012CPOL 22.5K   2   7
How to extend the MVC3 RemoteAttribute to validate server-side when JavaScript is disabled

In ASP.NET MVC3, the RemoteAttribute class works seamlessly with unobtrusive JavaScript to asynchronously execute server side validation via AJAX.

However, one of the problems with this approach arises when JavaScript is disabled on the user's browser. A thorough approach to validation will not rely on JavaScript being enabled, but the traditional DRY approach suggested by many is simply to encapsulate your validation logic in a method and call this from both your RemoteAttribute controller action and also on your page submission code as a backup, calling ModelState.AddModelError() if the validation fails, passing the model back to the view to signal that validation failed. Sadly, this renders the separation of concerns within MVC validation redundant as the controller method is now responsible for performing validation as well as its intended business logic.

A simple approach to fixing this error is to extend the existing RemoteAttribute class, overriding the IsValid method to call your remote controller method from the server. The code sample below works well for controller methods that return a JsonResult containing a boolean value.

C#
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RemoteWithServerSideAttribute : RemoteAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        string controllerName = this.RouteData["controller"].ToString();
        string actionName = this.RouteData["action"].ToString();
        string[] additionalFields = this.AdditionalFields.Split(',');

        List<object> propValues = new List<object>();
        propValues.Add(value);
        foreach (string additionalField in additionalFields)
        {
            PropertyInfo prop = validationContext.ObjectType.GetProperty(additionalField);
            if (prop != null)
            {
                object propValue = prop.GetValue(validationContext.ObjectInstance, null);
                propValues.Add(propValue); 
            }
        }

        Type controllerType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault
                              (t => t.Name.ToLower() == (controllerName + "Controller").ToLower());
        if (controllerType != null)
        {
            object instance = Activator.CreateInstance(controllerType);

            MethodInfo method = controllerType.GetMethod(actionName);

            if (method != null)
            {
                ActionResult response = (ActionResult)method.Invoke(instance, propValues.ToArray());

                ValidationResult output = null;

                if (response is JsonResult)
                {
                    bool isAvailable = false;
                    JsonResult json = (JsonResult)response;
                    string jsonData = json.Data.ToString();

                    bool.TryParse(jsonData, out isAvailable);

                    if (!isAvailable)
                    {
                        return new ValidationResult(this.FormatErrorMessage
                                                   (validationContext.DisplayName));
                    }
                    else
                    {
                        return null;
                    }
                }
            }
        }

        return null;
    }    

    public RemoteWithServerSideAttribute(string routeName)
        : base()
    {
    }

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

    public RemoteWithServerSideAttribute(string action, string controller, string areaName)
        : base(action, controller, areaName)
    {
    }
}
This article was originally posted at http://dotnetadventurer.blogspot.com/feeds/posts/default

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer The Test Factory
United Kingdom United Kingdom
Experienced web and software developer who has worked for some of the best and most respected software companies in the North of England as well as spending several years self employed.

Extensive knowledge of desktop and web programming languages, particularly C#, VB.NET, JavaScript, XML, CSS, Web Services, ASP.NET.

Comments and Discussions

 
QuestionController with Dependency Injection Pin
Rex Mahel19-Jun-19 7:27
Rex Mahel19-Jun-19 7:27 
Questionits working when I put the class inside my view model project. But not working when I put the same in a separate re usable library? Pin
sukeshchand24-Sep-14 21:04
professionalsukeshchand24-Sep-14 21:04 
AnswerRe: its working when I put the class inside my view model project. But not working when I put the same in a separate re usable library? Pin
ThommPL11-Jul-16 8:06
ThommPL11-Jul-16 8:06 
GeneralRe: its working when I put the class inside my view model project. But not working when I put the same in a separate re usable library? Pin
sukeshchand15-Jul-16 3:40
professionalsukeshchand15-Jul-16 3:40 
GeneralMy vote of 5 Pin
Rafael Merlin17-Mar-14 2:12
Rafael Merlin17-Mar-14 2:12 
GeneralRe: My vote of 5 Pin
Rafael Merlin25-Mar-14 11:04
Rafael Merlin25-Mar-14 11:04 
GeneralRe: My vote of 5 Pin
Rafael Merlin26-Mar-14 1:58
Rafael Merlin26-Mar-14 1:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.