Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using [FromUri] attribute - bind complex object with nested array

I want to send a complex object with a nested array in the uri to an MVC action method in a GET request.

Consider the following code:

 public ActionResult AutoCompleteHandler([FromUri]PartsQuery partsQuery){ ... }

 public class PartsQuery
 {
     public Part[] Parts {get; set; }
     public string LastKey { get; set; }
     public string Term { get; set; }
 }

 $.ajax({ 
    url: "Controller/AutoCompleteHandler", 
    data: $.param({                                        
                      Parts: [{ hasLabel: "label", hasType: "type", hasIndex : 1 }],
                      LastKey : "Last Key",
                      Term : "Term"                             
                   }),
    dataType: "json", 
    success: function(jsonData) { ... }
 });

This works just fine and binds correctly using the default model binder in MVC Web Api.

However, switch this to plain MVC not WebApi and the default model binder breaks down and cannot bind the properties on objects in the nested array:

Watch List

partsQuery      != null          //Good
--LastKey       == "Last Key"    //Good
--Term          == "Term"        //Good
--Parts[]       != null          //Good
----hasLabel    == null          //Failed to bind
----hasType     == null          //Failed to bind
----hasIndex    == 0             //Failed to bind

I would like to know why this breaks down in plain MVC and how to make FromUriAttribute bind this object correctly in plain MVC

like image 248
parliament Avatar asked Jul 10 '13 19:07

parliament


1 Answers

Core issue here is that MVC and WebApi use different model binders. Even base interfaces are different.

Mvc - System.Web.Mvc.IModelBinder
Web API - System.Web.Http.ModelBinding.IModelBinder

When you send data with your $.ajax call, you are sending following query string parameters:

Parts[0][hasLabel]:label
Parts[0][hasType]:type
Parts[0][hasIndex]:1
LastKey:Last Key
Term:Term

While, proper format that would bind with MVC default model binder has different naming convention for parameter names:

Parts[0].hasLabel:label
Parts[0].hasType:type
Parts[0].hasIndex:1
LastKey:Last Key
Term:Term

So, this method call would work:

$.ajax({ 
    url: "Controller/AutoCompleteHandler?Parts[0].hasLabel=label&Parts[0].hasType=type&Parts[0].hasIndex=1&LastKey=Last+Key&Term=Term",
    dataType: "json", 
    success: function(jsonData) { ... }
});

You need to construct your query string respecting MVC model binder naming conventions.

Additionally [FromUri] attribute in your example action is completely ignored, since it's not known to MVC DefaultModelBinder.

like image 83
Nenad Avatar answered Sep 26 '22 00:09

Nenad