Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Extending the MVC3 RemoteAttribute to validate server-side when JavaScript is disabled

, 5 Apr 2012
Rate this:
Please Sign up or sign in to vote.
CodeProjectIn 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 wil
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.

[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)
    {
    }

}

License

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

Share

About the Author

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

 
GeneralMy vote of 5 PinmemberRafael Merlin17-Mar-14 2:12 
GeneralRe: My vote of 5 PinmemberRafael Merlin25-Mar-14 11:04 
GeneralRe: My vote of 5 PinmemberRafael Merlin26-Mar-14 1:58 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 5 Apr 2012
Article Copyright 2012 by Simon Pickersgill
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid