I want to post some Products that has a ID and some Categories with jQuery. But I get a error in: Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder.BindProperty (NullReferenceException), when I add the Categories.
Should not the default ModelBinder be able to bind this (without a ActionFilter or custom ModelBinder)?
I tried to apply a ActionFilter (to deserialize) that I found in another SO thread, but it never runs. I have also tried with jQuery.ajaxSettings.traditional = true;, jQuery 1.3.2 and 1.4.2. And in the other examples I have found, they are just posting ID, Name, etc, not another array of complex objects.
Any ideas?
Classes
public class Product
{
public int ID { get; set; }
public Category[] Categories { get; set; }
}
public class Category
{
public int ID { get; set; }
}
HTML
<input id="Product[0]_ID" name="Product[0].ID" type="hidden" value="9" />
<input id="Product[0]_Categories[0]_ID" name="Product[0].Categories[0].ID" type="hidden" value="99" />
<in开发者_如何转开发put id="Product[1]_ID" name="Product[1].ID" type="hidden" value="8" />
<input id="Product[1]_Categories[0]_ID" name="Product[1].Categories[0].ID" type="hidden" value="88" />
Controller
[JsonFilter(Parameter = "p")]
public JsonResult GetProductPrice([Bind(Prefix = "Product")] Product[] p)
{
// TODO: Implement some checking...
return Json(true);
}
jQuery
$.post(getProductPriceUrl, $("form").serializeArray(), function(data) {
$("#Price").html(data);
});
JsonFilter
public class JsonFilter : ActionFilterAttribute
{
public string Parameter { get; set; }
//public Type JsonDataType { get; set; }
private JavaScriptSerializer serializer;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
serializer = new JavaScriptSerializer();
if (filterContext.HttpContext.Request.ContentType.Equals("application/json"))
{
string inputContent;
using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream))
{
inputContent = sr.ReadToEnd();
}
var result = serializer.Deserialize<Product>(inputContent);
filterContext.ActionParameters[Parameter] = result;
}
}
}
POST #1
__RequestVerificationToken=sz%2BLKCzTmdGMrH3TdOYipS5z%2BJ3uVyzBtJRZrruJoUohoGaH2O3DU5%2FcuU6hX1E%2F&Product%5B0%5D.ID=9&Product%5B0%5D.Categories%5B0%5D.ID=99&Product%5B1%5D.ID=8&Product%5B1%5D.Categories%5B0%5D.ID=88
POST #2
__RequestVerificationToken=sz+LKCzTmdGMrH3TdOYipWTERHSdtCvGUhuw/dGIkgSL3rjcSLO7RJJN/rcssVwv&Product[0].ID=9&Product[0].Categories[0].ID=99&Product[1].ID=8&Product[1].Categories[0].ID=88
POST #3
[{"name":"__RequestVerificationToken","value":"sz+LKCzTmdGMrH3TdOYipcqr8WKC2eL7CRS5BZUtwzD60WkqfnjdeAcO3DQg5ss6"},{"name":"Product[0].ID","value":"9"},{"name":"Product[0].Categories[0].ID","value":"99"},{"name":"Product[1].ID","value":"8"},{"name":"Product[1].Categories[0].ID","value":"88"}]
There are two ways I can think of to accomplish the goal of sending a complex object graph back to a the server properly wired up. One is the way you're attempting originally, that is to express the relationship in html. The other is to pass to partial views the child objects.
So you would create a partial view for a list of Categories and pass the Product.categories to it. That should take care of wiring it up properly for you.
It would be
@Html.Partial("_myCategoriesPartialView", Model.Categories)
That view of course would iterate over the collection. I believe if you want to do it the other way with html it would look like.
<input id="Product[0]_ID" name="Product[0].ID" type="hidden" value="9" />
should be
<input id="Product[0].ID" name="Product[0].ID" type="hidden" value="9" />,
but the way to tell for sure is to look at the Response.Form data
精彩评论