5

How can I pass dynamic data with an AJAX call to an MVC Controller?

Controller:

public JsonResult ApplyFilters(dynamic filters){
   return null;
}

The AJAX call:

$(':checkbox').click(function (event) {
    var serviceIds = $('input[type="checkbox"]:checked').map(function () {
        return $(this).val();
    }).toArray();

    //alert(serviceIds);

    $.ajax({
        type: 'GET',
        url: '/home/ApplyFilters',
        data: JSON.stringify({
            name: serviceIds
        }),
        contentType: 'application/json',

        success: function (data) {
            alert("succeeded");
        },
        error: function (err, data) {
            alert("Error " + err.responseText);
        }
    });

    //return false;
});

Ideally would be that the filters would contain the serviceIds as a property

For example like this: filters.ServiceIds. I got another filter for a date range and that one would be added like so: filters.DateRange.

And server side get the filter as a dynamic object in the ApplyFilters()

4
  • What does the filters object look, if your call the action with the AJAX call? Commented Oct 27, 2013 at 9:47
  • It looks like this: {object} and it can't be expanded. Commented Oct 27, 2013 at 10:18
  • dynamic objects are evaluated at the run time so change the return to return Json(filters) so that you can see the actual data returned in the response. Commented Oct 27, 2013 at 10:32
  • True, but I can just cast it too since I already know what type the object is. But that's failing because some how the data isn't passed 'good'. Commented Oct 27, 2013 at 13:09

1 Answer 1

7

Ad far as i know the default ASP MVC model binder cannot accomplish such a thing.

Fortunately there are numerous solutions, here are two of them :

1. Here is the first one, it converts your JSON data into a IDictionary<string, object> instance before passing it to your controller action. You would end up with :

public JsonResult ApplyFilters(IDictionary<string, object> filters)
{
   return null;
}

2. The second approach enables your controller action to receive a JsonValue instance. Making your action looks like this one :

public JsonResult ApplyFilters([DynamicJson]JsonValue filters)
{
   return null;
}

3. Based on the previous tutorials, you could create your own custom model binder returning a dynamic object. The following code has been taken from the first proposal and modified to generate a dynamic object. It is just to illustrate the idea, don't use it as is :

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
    {
        return null;
    }

    controllerContext.HttpContext.Request.InputStream.Position = 0;
    using (var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream))
    {
        var json = reader.ReadToEnd();
        if (string.IsNullOrEmpty(json))
        {
            return null;
        }
        dynamic result = new ExpandoObject();
        var deserializedObject = new JavaScriptSerializer().DeserializeObject(json) as IDictionary<string, object>;
        if (deserializedObject != null)
        {
            foreach (var item in deserializedObject)
            {
                ((IDictionary<string, object>)result).Add(item.Key, item.Value);
            }
        }
        return result;
    }
}

These solutions rely on building a custom model binder to handle this particuliar scenario :

  • Custom model binder is just an implementation of the IModelBinder interface in charge of parsing JSON posted data and converting them into another format.
  • ASP MVC framework is told to use these specific custom model binders by registering them (with ModelBinders.Binders.Add or using a dedicated attribute (CustomModelBinderAttribute).
Sign up to request clarification or add additional context in comments.

3 Comments

But I have seen this working exactly as how I described it in another project I have worked on. So I know this is possible for a 100%!
But doesn't the client side code needs a code refactor for it to work?
These custom model binders are expecting and parsing JSON. And it's exactly what you're providing to the server with your assignment "data: JSON.stringify({ name: serviceIds })". So no client code refactor should be required.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.