Knockout and MVC3: Posting JSON to action, seriali

2019-05-19 00:09发布

问题:

I've got a Knockout Model that gets posted via a save method:

 self.save = function(form) {
        ko.utils.postJson($("form")[0], self);
    };

I check the request to make sure all the data is properly being posted (it is):

However, when I get to my action:

 [HttpPost]
 public ActionResult Create(EquipmentCreateModel equipmentCreateModel)
 {
    /stuff here
 }

BuildingCode and Room contain escaped quotes, and identifiers is totally not null but has a count of 0:

And my ModelState is not valid, there is one error, for the Identifiers property which has an attempted value of :

and the Exception message is:

"The parameter conversion from type 'System.String' to type 'System.Collections.Generic.KeyValuePair`2[[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' failed because no type converter can convert between these types."

My Model:

public class EquipmentCreateModel
{
//used to populate form drop downs
public ICollection<Building> Buildings { get; set; }
public ICollection<IdentifierType> IdentifierTypes { get; set; }

[Required]
[Display(Name = "Building")]
public string BuildingCode { get; set; }

[Required]
public string Room { get; set; }

[Required]
[Range(1, 100, ErrorMessage = "You must add at least one identifier.")]
public int IdentifiersCount { get; set; } //used as a hidden field to validate the list
public string IdentifierValue { get; set; } //used only for knockout viewmodel binding

public IDictionary<Guid, string> Identifiers { get; set; }
}

Now first I thought it was a problem with knockout, but then I found out the data wasn't being posted in the request correctly. I fixed that and still had the same problem. I thought MVC3 automatically converts Json now? Why are my simple properties appearing in escaped quotes and why can't my identities collection properly populate from the posted data?

回答1:

Try this:

 [HttpPost]
 public ActionResult Create([FromJson] EquipmentCreateModel equipmentCreateModel)
 {
    //stuff here
 }

where FromJson is:

   public class FromJsonAttribute : CustomModelBinderAttribute
    {
      private readonly static JavaScriptSerializer serializer = new JavaScriptSerializer();
      public override IModelBinder GetBinder()
      {
         return new JsonModelBinder();
      }

      private class JsonModelBinder : IModelBinder
      {
         public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
         {
           var stringified = controllerContext.HttpContext.Request[bindingContext.ModelName];
           if (string.IsNullOrEmpty(stringified))
                return null;
           return serializer.Deserialize(stringified, bindingContext.ModelType);
          }
     }
 }

This is taken from: http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/ you should check the comments to as there are some modification for the FromJsonAttribute.



回答2:

If you are using MVC3 you don't need to add JsonValueProviderFactory. For those of us who are still on MVC2 you can add JsonValueProviderFactory manually http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx

However JsonValueProvider only works with AJAX post. For the full postback it needs the extra processing. There's a thread describing how to handle full postback: groups.google.com/d/topic/knockoutjs/3FEpocpApA4/discussion

The easiest solution would be to use AJAX post. Change

ko.utils.postJson($("form")[0], self);

to

$.ajax({
  url: $("form").action,
  type: 'post',
  data: ko.toJSON(self),
  contentType: 'application/json',
  success: function (result) {
     alert(result);
  }
});


回答3:

You could try:

[HttpPost]
 public ActionResult Create(string equipmentCreateModelString)
 {
    var equipmentCreateModel = JsonConvert.DeserializeObject<EquipmentCreateModel> equipmentCreateModelString, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
 }

Otherwise you need to use a JsonValueProviderFactory. Here's an example



回答4:

@DOTang, i have another approach. First, you need a clean js object from your view model. You can get it calling: ko.mapping.toJS(self), then pass your view model to postJson function as a property. Finally add [FromJson] attribute to your controller. Your controller argument name, must be equal to your js property name, in this case: model.

I hope it works for you as works for me.

server

[HttpPost]
public ActionResult RegisterUser([FromJson] EquipmentCreateModel model)
{
    //...
}

client

self.save = function() {
    var jsModel = ko.mapping.toJS(self);
    ko.utils.postJson('@Url.Action("Create", "Equipment")', { model : jsModel });
}