Sending JSON to an ASP.NET MVC Action Method Argument

Javier “G Money” Lozano, one of the good folks involved with C4MVC, recently wrote a blog post on posting JSON (JavaScript Object Notation) encoded data to an MVC controller action. In his post, he describes an interesting approach of using a custom model binder to bind sent JSON data to an argument of an action method. Unfortunately, his sample left out the custom model binder and only demonstrates how to retrieve JSON data sent from a controller action, not how to send the JSON to the action method. Honest mistake. :)

His post reminds me of how remiss I’ve been in blogging recently because a while back, we added something to our ASP.NET MVC 2 Futures library that handles sending JSON to an action method but I just never found time to blog about it.

There’s one key problem with using a model binder to accept JSON. By writing a custom model binder, you miss out on validation. Using his example, if you type “abc” for the Age field, you will get a serialization failure when attempting to serialize the JSON into the PersonInputModel object because Age is an Int32 and the serialization will fail.

Value Providers to the rescue!

This is where value providers, a new feature of ASP.NET MVC 2, enters to save the day. Whereas model binders are used to bind incoming data to an object model, value providers provide an abstraction for the incoming data itself.

When the ASP.NET MVC feature team first implemented value providers, Jonathan Carter and I were working on a client templating sample which sent JSON to an action method. Rather than write a custom model binder which was the approach I took, Jonathan had the unique insight to write a custom value provider which received JSON data and serialized it to a dictionary rather than the target object. The beauty of his approach is that this dictionary data is then passed to the default model binder which binds it to the final object with validation!

I took is his prototype and added the JsonValueProviderFactory to our ASP.NET MVC 2 Futures library and then totally didn’t write about it. Yes, I suck.

Setting it up

To get started, download the ASP.NET MVC 2 Futures Library and reference the Microsoft.Web.Mvc.dll assembly. Then, in your Global.asax.cs file, add the following call to register the JsonValueProviderFactory.

protected void Application_Start() 
{
  RegisterRoutes(RouteTable.Routes);
  ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
}

That’s it! You’re done!

This value provider will handle requests that are encoded as application/json. There’s no need to specify a model binder on classes that accept JSON input.

See it in action

I took the liberty of updating Javier’s sample to use this new value provider and to actually post JSON to the action method.

It turns out, sending JSON encoded data to an action method with jQuery was not as straightforward as I hoped. If you know of a more straightforward way, let me know. I ended up using a JSON plug-in for jQuery I found on the Internets. This provides a $.toJSON method I could use to serialize an object into a JSON encoded string. Here’s the updated client script code.

UPDATE: Per Dave Ward’s comment here I should be using json2.js and its JSON.stringify(...) method instead because it matches an API that some browsers implement and will use the native implementation if it exists. Nice! I’ll update this blog post later when I have a moment.

$(function () {
    $("#personCreate").click(function () {
        var person = getPerson();

        // poor man's validation
        if (person == null) {
            alert("Specify a name please!");
            return;
        }

        var json = $.toJSON(person);

        $.ajax({
            url: '/home/save',
            type: 'POST',
            dataType: 'json',
            data: json,
            contentType: 'application/json; charset=utf-8',
            success: function (data) {
                // get the result and do some magic with it
                var message = data.Message;
                $("#resultMessage").html(message);
            }
        });
    });
});

function getPerson() {
    var name = $("#Name").val();
    var age = $("#Age").val();

    // poor man's validation
    return (name == "") ? null : { Name: name, Age: age };
}

Notice that we use the $.ajax method to specify both the JSON data and the JSON content type for the request.

A quick check in Fiddler confirms that the data in the POST request is properly JSON encoded.

json-request-fiddler

Now, within my action method, I can actually check to see if the model state is valid and if not, return an error message.

[HttpPost]
public ActionResult Save(PersonInputModel inputModel) {
  if (ModelState.IsValid)
  {
    string message = string.Format("Created user '{0}' aged '{1}' in the system."
      , inputModel.Name, inputModel.Age);
    return Json(new PersonViewModel { Message = message });
  }
  else {
    string errorMessage = "<div class=\"validation-summary-errors\">" 
      + "The following errors occurred:<ul>";
    foreach (var key in ModelState.Keys) {
      var error = ModelState[key].Errors.FirstOrDefault();
      if (error != null) {
        errorMessage += "<li class=\"field-validation-error\">" 
         + error.ErrorMessage + "</li>";
      }
    }
    errorMessage += "</ul>";
    return Json(new PersonViewModel { Message = errorMessage });
  }
}

And as you can see in the Fiddler screenshot, I sent an invalid Age to the server and yet, it all still works.

validation-with-json

Whew! I can finally cross this off of my immense blog backlog. :) Hopefully soon, I’ll blog a more detailed write-up of value providers.

We have plans to add the JsonValueProviderFactory to ASP.NET MVC 3 so that it’s a built-in feature. I hope you find this useful and as always, let me know if there are ways we can improve it!

Oh, and here’s Javier’s updated sample with the value provider.

What others have said

Requesting Gravatar... Javier Lozano Apr 15, 2010 9:52 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Haha, great minds think a like. Someone on my blog post commented "you're not sending JSON", which the commenter (as yourself) are completely right.

So I wrote a simple sample that does the _same_ thing you just did only without an IValueProvider, just a plain JSONModelBinder. It's here - github.com/.../JSONModelBinder

However, your approach is much sexier and straight to the point.

-G out :)
Requesting Gravatar... Felipe Lima Apr 15, 2010 9:55 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Nice to know I can use JSON input and still keep the good old model binding in the action method.
Now, I'll just have to find out what'thahell a ValueProvider is :)
Requesting Gravatar... Dave Ward Apr 15, 2010 10:11 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Consider using json2.js instead of the $.toJSON() plugin. Its JSON.stringify() does the same thing.

The benefit of going that way is that browsers have duplicated json2.js' API in their browser-native JSON handling, and json2.js only defines its functions if the JSON object doesn't already exist. So in newer browsers, JSON.stringify() will automatically call the browser-native method.
Requesting Gravatar... haacked Apr 15, 2010 10:29 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Thanks Dave! When I get a chance I'll update my blog post. Thanks for the tip!
Requesting Gravatar... Mike Sampson (Sampy) Apr 15, 2010 11:30 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
I just wrote a custom value provider earlier this week actually. Mine offers up properties of the HttpRequest object (referrer, ip address, and user agent) for binding to anything that ends with the right string (so that way it can cheat a little bit for deeply nested objects that need those properties).

I use it to collect data for anonymous comments as well as for forum posts so we can do things like IP bans and looking for people with multiple accounts.

Handy new feature!
Requesting Gravatar... Sergejus Apr 15, 2010 12:12 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Phill,

I would also suggest for the getPage() function to use jQuery serialize method (http://api.jquery.com/serialize) instead of doing .val() manually.
Requesting Gravatar... Adam Apr 16, 2010 6:23 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
What is the advantage of doing this?

If you call $.ajax and pass a json object to the data parameter it will convert it to key/value pairs and the default model binder will take care of the binding. It seems that you are doing more worked than is needed.

Unless of course I'm missing something.

Requesting Gravatar... Bob Cravens Apr 16, 2010 2:50 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Why convert it to JSON on the client? Does the added complexity buy you something? Maybe you save a few bytes on the wire. When the data pops out the other side, it is converted to the model before your controller gets a whack at it. Why not keep it simpler:


var url = "/home/save";
var name = $("#Name").val();
var age = $("#Age").val();
$.post(url, { Name: name, Age: age }, function(data) {
var message = data.Message;
$("#resultMessage").html(message);
});


Same question for the return type. Why return JSON? In this case, it is a simple string. Why not just return the string.

Am I missing something?

Here is a simple to complex run down on Ajax calls to MVC controllers: blog.bobcravens.com/...

Requesting Gravatar... haacked Apr 16, 2010 6:44 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Why do it this way? Well in this specific case, I probably wouldn't. This was a contrived example. The real point of this post is to demonstrate how to receive JSON encoded data as an argument to your action method. Why?

If you were using the ASP.NET Ajax client templating preview, it sends objects to an URL as JSON objects. This solution I showed would allow you to connect client templates to an MVC URL. Other client templating solutions might do that.

Also, you can imagine an action method with this in place could support multiple clients: an HTML form posting form encoded data, a silverlight widget posting JSON, etc...
Requesting Gravatar... Shamol Apr 17, 2010 12:42 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
we are using Action Filter to do this for quite some time now. ( we do our validation on another tire). i wonder if my approach has any negative side to it?
Requesting Gravatar... Michael McGuire Apr 17, 2010 11:41 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
I must say, as the commenter on Javier's blog, that I giggled like a little girl when it appeared to inspire this blog post. Of course, I know you were already planning on doing it, but still.

Thanks for taking the time to discuss this issue Phil, I've been wondering what was a good way way to get JSON passed to an MVC Action method.
Requesting Gravatar... Justin Apr 18, 2010 6:59 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Thanks Phil,

Until now I have been binding the input as a string then using a JavaScript Serializer to transform it to my input model. This is so much easier.
Requesting Gravatar... Erik Apr 21, 2010 11:34 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
This is just what I've been looking for! But I can't find the actual code in the MvcFutures folder in the codeplex source?! Could you point me in the right direction?
Requesting Gravatar... John McDowall Apr 26, 2010 1:40 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
The Value provider doesn't seem to reserialise certain characters correctly, such as the @ symbol used in an email address. Is this expected?
Requesting Gravatar... Mike Apr 28, 2010 9:11 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
What is the json value provider using on the back end? Is it fully capable of binding json to complex object types (objects that have properties that are lists or that are other objects)?
Requesting Gravatar... Ben May 01, 2010 7:42 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
The JsonValueProviderFactory is great.

However (isn't is always a "however"), when I try to add an AntiForgeryToken to my data, I get a response back that my AntiForgeryToken was missing or invalid.

Even if I submit only the __RequestVerificationToken in the data, I still get this message using the JsonValueProviderFactory.

Has anyone else come across this? Any suggestions?
Requesting Gravatar... Richard Garside May 12, 2010 7:37 AM
# XML?
Is there a similar way to send XML to an MVC action method?

I'm creating an API and I want the API user to be able to use either json or xml.
Requesting Gravatar... JJ May 15, 2010 7:47 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
How can I tell if it is executing the JsonValueProviderFactory or not?

My JSON.stringify(...) method produces this string:

"{"Id":"3","OrderStatus"...Z","Comments":"rtgdds"}"

Is that the correct format? In my action, the POCO object is not null, but all of its fields are null
Requesting Gravatar... JJ May 16, 2010 2:58 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Why is it, that in order to send a json object to a controller method, you have to manually change the content type?

Ie, why does MVC not work out of the box with the default content type of application/x-www-form-urlencoded?
Requesting Gravatar... haacked May 16, 2010 4:27 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
We do work with application/x-www-form-urlencoded when you are sending form urlencoded data to the webserver.

But JSON Serialized data is not the same as form urlencoded data. The point of this post is how to send JSON encoded data as the BODY of your HTTP Post request and not in a form field. In that case, you need to change the content type.
Requesting Gravatar... Fabio Maulo May 22, 2010 9:28 AM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
This example fabiomaulo.blogspot.com/... do that in WebForms.
The method Publica(ClasificadoInfo clasificado) is the one receiving the POST in Json and transform it to an obj graph using a convention.
Requesting Gravatar... Michael Reyeros Jun 23, 2010 5:50 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
Good evening Phil, I tried implementing this code in a project that I working on but for some reason when the model gets to my ActionMethod for some reason decimal properties are being set to 0 although they have a different value and if I view the page source I can see the correct values are being passed to the stringify function. Here is the javascript code as it looks when it hits the page:

$("#VehicleModelId1").change(function () {
var model = $("#VehicleModelId1 > option:selected").attr("value");
$("#imgModel1").attr('src', "GetModelImage/" + model);
var services = $.toJSON([{"MobileProviderServiceId":1,"ServiceName":"Basic Service","SmallVehicleCost":15.00,"MediumVehicleCost":20.00,"LargeVehicleCost":25.00,"SmallVehicleDuration":15,"MediumVehicleDuration":20,"LargeVehicleDuration":25}]);
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "GetServicesBasedOnModel/" + model,
data: services,
dataType: "json",
async: true,
success: function (data) {
var serviceWrapper = $("#serviceWrapper1 > ul");
if (data) {
serviceWrapper.html(data.services);
}
else
serviceWrapper.html('');
}
});
});
When I set a breakpoint at my action method, the cost properties are all equal to 0 but the duration properties which are integer properties on my model, all have their correct values.

Hopefully you can lead me in the right direction.
Requesting Gravatar... Richard Garside Jul 15, 2010 11:41 AM
# My XML Value Provider
Inspired by this article I created an XML Value Provider. It's based on your source code. I've put the code on my site so hopefully other people will find it useful.

XML Value Provider
Requesting Gravatar... Daniel Steigerwald Aug 11, 2010 1:26 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
JSON.stringify({ Updated: new Date() });
Doesn't work.
Requesting Gravatar... Daniel Steigerwald Aug 11, 2010 2:21 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
More precisely, it doesn't work for get request.
Requesting Gravatar... Chris Aug 18, 2010 12:54 PM
# re: Sending JSON to an ASP.NET MVC Action Method Argument
I've had a few problems when using this method. First. what data types do these value providers map and how do they work really? I'm having problems mapping a return of a JSON Date to a DateTime field in my View Model. Also decimals don't seem to map when sending a 1.0 or even a "1.0". Lastly When sending over mildly large collections (200 entries 8 fields each) It takes us about 5 minutes for the controller to hit it's first break point. I am assuming this is because the data binding is taking forever (small collections work well). Any idea or insight?

What do you have to say?

(will show your gravatar)
Please add 2 and 6 and type the answer here: