代码改变世界

Post Complex JavaScript Objects to ASP.NET MVC Controllers

2011-12-27 13:23  朱峰(Peter.zhu)  阅读(497)  评论(0编辑  收藏  举报

Use the plug-in postify.js to handle posting complex JavaScript objects to ASP.NET MVC controllers using the default model binder

There is a lot on conversation going on about binding complex JavaScript objects to ASP.NET MVC actions.   Complex objects are objects that have sub objects and/or arrays.

Let’s assume the following complex model:

Complex Model

I have a Person object with some properties, an array of phone numbers and an Address object. I would like to pass a JavaScript representation of this object to our Controller’s Create action:

1[AcceptVerbs(HttpVerbs.Post)]
2public ActionResult Create(Person person)
3{
4    //Code to add the person goes here
5 
6    //return the view
7    return View();
8}

On the client, the JavaScript representation of a Person would be:

01var myPerson = {
02    FirstName: "Nick",
03    LastName: "Riggs",
04    Age: 29,
05    Address: {
06        Street: "2780 Somewhere Far",
07        City: "Birmingham",
08        State: "AL"
09    },
10    PhoneNumbers: [
11        "205-555-5634",
12        "205-555-2294",
13        "205-555-7681"
14    ]
15};

One way to send this object to our Controller is to “stringify” the object into a JSON string using a plugin like toJSON. However, this requires us to change the Action to accept a string instead of a typed parameter, and then deserialize the string using the JavaScriptSerializer. I can get around this by automating the deserialization with a custom ActionFilterAttribute or ModelBinder. But, what if I want to use the built-in DefaultModelBinder functionality?

The default model binding in ASP.NET MVC works based on form post data. For example, if I were going to post a simple version of Person and have ASP.NET MVC map it to our action’s person parameter, I could post:

1person.FirstName: Nick
2person.LastName: Riggs
3person.Age: 29

ASP.NET MVC does a good job of recognizing this post data as being a Person and mapping it as such. On top of that, it has its own simple yet powerful syntax for representing more complex objects, such as this:

1person.FirstName: Nick
2person.LastName: Riggs
3person.Age: 29
4person.PhoneNumbers[0]: 205-555-5634
5person.PhoneNumbers[1]: 205-555-5634
6person.PhoneNumbers[2]: 205-555-5634
7person.Address.Street: 2780 Somewhere Far
8person.Address.City: Birmingham
9person.Address.State: AL

So, instead of stringifying my JavaScript objects, I will postify them! (I made the word postify™ up, it’s mine now). My custom postify plug-in will do the work. Here is the source code:

01$.postify = function(value) {
02    var result = {};
03 
04    var buildResult = function(object, prefix) {
05        for (var key in object) {
06 
07            var postKey = isFinite(key)
08                ? (prefix != "" ? prefix : "") + "[" + key + "]"
09                : (prefix != "" ? prefix + "." "") + key;
10 
11            switch (typeof (object[key])) {
12                case "number"case "string"case "boolean":
13                    result[postKey] = object[key];
14                    break;
15 
16                case "object":
17                    if (object[key].toUTCString)
18                        result[postKey] = object[key].toUTCString().replace("UTC""GMT");
19                    else {
20                        buildResult(object[key], postKey != "" ? postKey : key);
21                    }
22            }
23        }
24    };
25 
26    buildResult(value, "");
27 
28    return result;
29};

This is the first cut of the plug-in, and I’m sure it’s missing something – I’ll update the source code as I make updates. That said, the plug-in greatly simplifies posting complex objects to ASP.NET MVC controllers. Here is a sample in jQuery that posts myPerson:

1$.ajax({
2    type: "POST",
3    url: "/People/Create",
4    data: $.postify(myPerson)
5});

That’s it!  The plugin will handle formatting the data in an ASP.NET MVC post-friendly manner. On the server side, the parameter inflates nicely using the default model binder:

Inflated-Person

If you need to post to an action that takes multiple parameters, the complex object must be prefixed with the name of the parameter – in our case, Person. To include another parameter, use this syntax:

1$.ajax({
2    type: "POST",
3    url: "/JSON/DoSomething",
4    data: $.postify({
5        person: myPerson,
6        otherParam: true
7    })
8});