Improved JavaScript Model Binding in ASP.NET MVC 3 Preview 1

Posted by: Malcolm Sheridan , on 10/8/2010, in Category ASP.NET MVC
Views: 61001
Abstract: A new addition to ASP.NET MVC 3 Preview 1 has been the improvement in model binding JSON data posted to an action through an Ajax form post. This makes it simple to post JSON data to any action method. Sometimes in ASP.NET MVC 2 this was difficult to do, but not anymore.
A new addition to ASP.NET MVC 3 Preview 1 has been the improvement in model binding JSON data posted to an action through an Ajax form post. This makes it simple to post JSON data to any action method. Sometimes in ASP.NET MVC 2 this was difficult to do, but not anymore. Instead of me talking about it, let's jump into an example to show you how the improvement works. To see this in action, you'll need to download ASP.NET MVC 3 Preview 1 which can be found here. For beta software, I always recommend installing this on a virtual machine, just in case something goes wrong.
Open Visual Studio 2010 and create a new ASP.NET MVC 3 Web Application (Razor) project. I've got a simple class that is going to be the model and it is an Order. Here's the properties of this class:
 
public class Order
{
      public int Id { get; set; }
       public string GivenName { get; set; }
       public string Surname { get; set; }
       public string Address { get; set; }
       public DateTime DateOrdered { get; set; }
       public int Quantity { get; set; }
 
        public static List<Order> GetOrders()
        {
            return new List<Order>
                       {
                           new Order { Id = 1, GivenName = "John", Surname = "Smith", Address = "100 Tom Street", DateOrdered = DateTime.Now.AddDays(-2), Quantity = 3 },
                           new Order { Id = 2, GivenName = "Steve", Surname = "Hello", Address = "777 Hello Lane", DateOrdered = DateTime.Now.AddDays(-7), Quantity = 15 },
                           new Order { Id = 3, GivenName = "Kevin", Surname = "Rudd", Address = "666 Lane", DateOrdered = DateTime.Now.AddDays(12), Quantity = 8 }
                       };
        }
}
 
There's a static method called GetOrders that returns a list of orders. Nothing special yet. The next step is to create a form that allows the user to edit the data then post it to the server. Here it is:
 
<form method="post" action="/Order/Edit">
      <fieldset>
            <legend>Fields</legend>
                             
            @Html.EditorForModel();
           
            <p>
                <input type="submit" value="Save" />
            </p>
       </fieldset>
</form>
 
I'm making full use of the built-in EditorTemplates in MVC 2. To make this application really slick, when the user hits the submit button, I want to do something called Form Hijacking. This is a process of making an Ajax request perform the HTTP post, thus eliminating the need for a full page refresh. Here's the JavaScript:
 
$(function () {   
    $("form:first").submit(function (e) {
        e.preventDefault();      
        var order = {
            Id: $("#Id").val(),
            GivenName: $("#GivenName").val(),
            Surname: $("#Surname").val(),
            Address: $("#Address").val(),
            DateOrdered: $("#DateOrdered").val(),
            Quantity: $("#Quantity").val()
        };
 
        $.ajax({
            url: $(this).attr("action"),
            type: "POST",
            data: JSON.stringify(order),
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            success: function (data) {
               
            }
        });
    });
});
 
In the code above I'm creating an object literal and passing that as the data to the Ajax request. All looks good until you run the code. If you put a breakpoint on the Edit/Action method, you'll notice the parameter is empty:
 
Image2
 
This is happening because the framework can't model bind the JSON data to the .Net object. The DefaultModelBinder is the class responsible for this. Thankfully there's a simple fix. ASP.NET MVC 3 Preview 1 has a new factory class called ValueProviderFactories. This class is a value provider which means it gathers values from different sources for an action method. The factory to model bind the JSON data is JsonValueProviderFactory. You ad this new value provider factory to your global.asax file:
 
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
 
Now when you run your code, you'll see the data is being sent correctly to the action:
 
Image_1
 
What this code means is whenever an action receives parameters from JSON, it will use the JsonValueProviderFactory class to break down the data and transform that into something more meaningful to you. If you need to do something similar in ASP.NET MVC 2, you could write a custom JSON model binder like the following:
 
public class JsonOrderModelBinder : IModelBinder
{
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                // not JSON request
                return null;
            }
 
            var request = controllerContext.HttpContext.Request;
            var incomingData = new StreamReader(request.InputStream).ReadToEnd();
 
           if (String.IsNullOrEmpty(incomingData)) {
                // no JSON data
                return null;
            }
           
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return serializer.Deserialize(incomingData, bindingContext.ModelType);
        }
}
 
And to use it do this:
 
[HttpPost]
public ActionResult Edit([ModelBinder(typeof(JsonOrderModelBinder))] Order order)
{
      return View();
}
 
Both achieve the same result, but using the JsonValueProvider Factory means you register it once and that's it. This makes it simple to receive JSON data.
 
The entire source code of this article can be downloaded over here
Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Malcolm Sheridan is a Microsoft awarded MVP in ASP.NET, a Telerik Insider and a regular presenter at conferences and user groups throughout Australia and New Zealand. Being an ASP.NET guy, his focus is on web technologies and has been for the past 10 years. He loves working with ASP.NET MVC these days and also loves getting his hands dirty with jQuery and JavaScript. He also writes technical articles on ASP.NET for SitePoint and other various websites. Follow him on twitter @malcolmsheridan


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by osmirnov.net on Saturday, October 9, 2010 6:01 AM
Good post. But instead of IsNullOrEmpty() method better to use IsNullOrWhiteSpace() method.
Comment posted by Malcolm Sheridan on Saturday, October 9, 2010 10:23 PM
@osmirnov.net
Good tip.  Thanks for that!
Comment posted by Bret Ferrier @runxc1 on Tuesday, October 12, 2010 9:19 AM
While this is a good example of the JSONValueProviderFactory there is a much easier way to perform Form Hijacking using jQuery.  If you use the jQuery Serialize method on the form you can easily get all of the forms element and post them to the action without using JSON which means that you could in essence use the same Action to respond to a Regular Post Back and an Ajax call with half the JavaScript needing to be written.
Comment posted by Malcolm Sheridan on Tuesday, October 12, 2010 3:23 PM
@Bret Ferrier
This is just an example of how to use the JsonValueProvider.  Would I post data this way?  Probably not all the time, but 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.  

Just demonstrating another way to do things.
Comment posted by Nirjhor on Tuesday, October 19, 2010 12:32 AM
Thanx.it was a wonderful post.
Comment posted by numnut on Thursday, October 28, 2010 4:58 PM
you dont have to do any of that.  if you change in your first example
var order = {
            Id: $("#Id").val(),
            GivenName: $("#GivenName").val(),
            Surname: $("#Surname").val(),
            Address: $("#Address").val(),
            DateOrdered: $("#DateOrdered").val(),
            Quantity: $("#Quantity").val()
        };

to
var order = { order :{
            Id: $("#Id").val(),
            GivenName: $("#GivenName").val(),
            Surname: $("#Surname").val(),
            Address: $("#Address").val(),
            DateOrdered: $("#DateOrdered").val(),
            Quantity: $("#Quantity").val()
        }};
which just wraps the object in a name the deserializer can recognize, it will work fine. oh, 1 more detail is you dont need to JSON.stringify this. just data: order in your ajax call.
Comment posted by Malcolm Sheridan on Thursday, October 28, 2010 5:00 PM
@numnut
Yes that's correct, I was just showing how if you don't do that the JsonValueProvider will take care of everything for you.

Thanks for the code though!
Comment posted by Tim Ellison on Thursday, February 24, 2011 7:43 PM
Better late than never.  One of my colleagues today was talking about .NET curry.  I've been struggling lately to fully understand how model binding truly works having been beat up about the head and neck working with viewmodels that have nested collections of other view models.  Started on your blog site to do a deep dive of model binding.  I think you've gotten me headed in the right direction.
Comment posted by Cody Ruhl on Friday, February 25, 2011 1:35 AM
+1 Well said Tim! I love all the MVC + jquery + sharepoint articles. It's the first site I open every morning before starting work ;)
Comment posted by James Taylor on Friday, September 2, 2011 2:44 AM
What if you need to do this in mvc 1
Comment posted by ubwebs on Thursday, December 8, 2011 4:15 AM
A new addition to ASP.NET MVC 3 Preview 1 has been the improvement in model binding JSON data posted to an action through an Ajax form post. This makes it simple to post JSON data to any action method. Sometimes in ASP.NET MVC 2 this was difficult to do, but not anymore. Instead of me talking about it, let's jump into an example to show you how the improvement works. To see this in action, you'll need to download ASP.NET MVC 3 Preview 1 which can be found here. For beta software, I always recommend installing this on a virtual machine, just in case something goes wrong.
http://www.ubwebs.com/
<a href="http://www.ubwebs.com/">Web Application Development</a>

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel