MVC 3 JSON string not serializing into Controller action - asp.net-mvc

Currently using MVC3 and using jQuery $.post function to send the ajax request to the controller action called "SearchInitiated". I'm having a little bit of a head-scratcher here because I'm not sure exactly where my issues lies. I'm sure its something minor that I have overlooked.
When I call my Controller method from an AJAX call, I am passing a json object (stringified) to a controller action. See below:
Request Headers from Chrome
Accept:/ Content-Type:application/x-www-form-urlencoded;
charset=UTF-8 User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64)
AppleWebKit/537.17 (KHTML, like Gecko)
Chrome/24.0.1312.52 Safari/537.17
X-Requested-With:XMLHttpRequest
Form Dataview
connectorId:1
sessionId:97e2d8b2-be9e-4138-91ed-42ef9c6a2cd6
party:
{"Tag":null,"PartyIdentifier":"1","Address":null,"Address2":null,"BusinessName":"","CellPhoneNumber":null,"CIF":"","City":null,"Country":null,"DateOfBirth":null,"EMailAddress":null,"EmploymentTitle":null,"EmployerAddress":null,"EmployerAddress2":null,"EmployerCity":null,"EmployerName":null,"EmployerPhoneNumber":null,"EmployerState":null,"EmployerZip":null,"Fax":null,"Firstname":"TEST","Lastname":"USER","MailingAddress":null,"MailingAddress2":null,"MailingCity":null,"MailingState":null,"MailingZip":null,"Middlename":null,"PhoneNumber":null,"State":null,"TIN":"1111111","WorkPhoneNumber":null,"Zip":null}
javascript
var parties = #(Html.Raw(Json.Encode(Model.SearchParties)));
function SearchForCustomer(id)
{
var currentSelectedParty = GetPartyById(id)
//SearchPost is a wrapper for $.ajax using default values for dataType and contentType
SearchPost(URL, {
'connectorId': '#Model.ConnectorId',
'sessionId': '#Model.SessionId',
'party' : JSON.stringify( currentSelectedParty )
}
}
Controller
public ActionResult SearchInitiated(int connectorId, string sessionId, SearchParty party)
{
code here....
}
public class SearchParty
{
public SearchParty();
public SearchParty(string lastname, string firstname);
public string Address
{
get;
set;
}
public string City
{
get;
set;
}
public string Country
{
get;
set;
}
public string DateOfBirth
{
get;
set;
}
.... etc etc
}
However, the party object is null.
If I change the code to the following, everything deserializes correctly into the strongly typed object.
public ActionResult SearchInitiated(int connectorId, string sessionId, string party)
{
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
SearchParty sp =
json_serializer.Deserialize<SearchParty>(party);
}
I know my json string is valid since it is working in the second code snippet and the value is being passed in the call. What else could I be missing?

Try this.
public class SearchParty
{
public string party { get; set; }
}
[HttpPost]
public ActionResult SearchInitiated(SearchParty party)
{
....
return View();
}

probably you need to set the traditional prop of jQuery.ajax to true in order to achieve traditional style of param serialization
put the below line of code immediatly after the document ready like
$(function(){
jQuery.ajaxSettings.traditional = true;
});
This SO question may help you further

I would make sure you have the [Serializable] attribute on your model. Also, make sure your request specifies party={myjson} .

You just need to declare a class 'SearchParty' in the controller to retrieve the strongly typed object in the controller without serializing.
public class SearchParty
{
public string party { get; set; }
}
public ActionResult SearchInitiated(SearchParty party)
{
code here....
}
Please check this link too

I resolved my error by modifying the javascript ajax call to this:
SearchPost(URL, JSON.stringify( {
'connectorId': '#Model.ConnectorId',
'sessionId': '#Model.SessionId',
'party' : currentSelectedParty
}))
I needed to stringify the entire data object sent in the ajax call, not just the 'party' object

Related

Json and ASP.NET MVC Model Binding

I have a UI that looks like this:
I am trying to data of the newly created row to the server so that the server may save it.
I am sending data in JSON format from the client to my MVC application. Here's my ajax request:
var values = []; // an array with each item being an object/associative array
// more code to get values into variables
for (var i = 0; i < cultures.length; i++) {
var cultureName = cultures[i];
var valueTextBox = $(row).find(...);
var value = $(valueTextBox).val();
var cultureNameAndValue = { 'CultureShortName' : cultureName, 'StringValue' : value };
values.push(cultureNameAndValue);
}
var stringTableRow =
{
'ResourceKeyId': resourceKeyId,
'Key': resourceKeyName,
'CategoryId': categoryId,
'CategoryName': categoryName,
'StringValues': values
};
var stringified = JSON.stringify({ StringTableRow: stringTableRow });
$.ajax('/Strings/JsonCreateNew',
{
cache: false,
async: false,
type: 'POST',
contentType: 'application/json; charset=UTF-8',
data: stringified,
dataType: 'json',
error: SaveNewResourceClientSideHandler.OnError,
success: SaveNewResourceClientSideHandler.OnSuccess
});
Here's the data it sends (as seen in Firebug):
{"StringTableRow":{"ResourceKeyId":"","Key":"Foo",
"CategoryId":"1","CategoryName":"JavaScript",
"StringValues":[
{"CultureShortName":"en-US","StringValue":"Something"},
{"CultureShortName":"fr-FR","StringValue":""}]
}}
Here's my server side code:
public ActionResult JsonCreateNew(StringTableRow row)
{
// CreateNewStringTableRow(row);
// return a success code, new resource key id, new category id
// return Json(
new { Success = true, ResourceKeyId = row.ResourceKeyId,
CategoryId = row.CategoryId },
JsonRequestBehavior.AllowGet);
return new EmptyResult();
}
And here's the business object that I want my incoming POST'ed data to be bound to:
public class StringTableRow
{
public StringTableRow()
{
StringValues = new List<CultureNameAndStringValue>();
}
public long ResourceKeyId { get; set; }
public string Key { get; set; }
public long CategoryId { get; set; }
public string CategoryName { get; set; }
public IList<CultureNameAndStringValue> StringValues { get; set; }
}
public class CultureNameAndStringValue
{
public string CultureShortName { get; set; }
public string StringValue { get; set; }
}
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
}
}
Problem:
The action JsonCreateNew receives an object that is not null but has all properties uninitialized, i.e all nullable properties are null and value properties are at their default values. Therefore, effectively I get no data at all even when the client sends a perfectly valid Json string.
Do I need to do custom model binding?
Okay, I solved my problem and this might be an important insight for other programmers as well, because I learnt something while solving my own problem.
My solution:
I resorted to not using JSON and instead used the default encoding that HTTP uses for encoding form posted values, and then I used something that smacks of custom model binding (without actually creating a model binder).
Explanation:
Normally, when you make an ajax request and pass data from the client to any server platform, if you do not specify the encoding, i.e. the contentType parameter in the settings object of jQuery's ajax method (or if you are using any other means to make the ajax request other than jQuery, then however that thing sets the ContentType HTTP header is what you're after), HTTP encodes your posted data using its default encoding, which is much like what you post with a GET request after the query string, only it is binary encoded and not sent as a part of the URL.
If that's the case, and you're posting a collection of any type (IList, IEnumerable, ICollection, IDictionary, etc.) to ASP.NET MVC, then don't create an associative array in JavaScript to embody the collection. Don't even create an array. Don't create anything.
In fact, just pass it along inside the main big object using the convention:
data =
{ /*scalar property */ Gar: 'har',
/* collection */ 'Foo[index++].Bar' : 'value1',
'Foo[index++].Bar' : 'value2'
}
And don't use JSON. That will solve half your problem.
On the server side, receive the posted data in a FormCollection and use its IValueProvider methods (GetValue) to dig out the data you need. You don't have to explicitly create a model binder to do it. You can do it in a private method of your controller itself.
I will improve this answer a bit later when I can find more time.
Use Backbone or KnockoutJs for data binding.

MVC4 RC WebApi parameter binding

I upgraded from MVC4 beta to RC and the latest autofac. The following action was binding properly, but now both parameters are null. I see they changed things about the Formatters and such but I am not sure what caused my problem
[HttpPost]
RedirectModel MyAction(string value1, string value1)
REQUEST
Method: POST
Accept: application/json
URL: api/controller/myaction
BODY: {"value1":"1000", "value2":"foo"}
When you want to avoid using a DTO object, try this:
[HttpPost]
RedirectModel MyAction(dynamic value1, dynamic value2) {
string sValue1 = value1;
string sValue2 = value2;
Not really sure why the change from Beta, but I was able to make it work by changing the action signature to:
[HttpPost]
RedirectModel MyAction(MyActionDTO dto)
and defining MyActionDTO as
public class MyActionDTO
{
public string value1 { get; set; }
public string value2 { get; set; }
}
It was throwing an exception about not being able to bind to multiple body parameters using the two string paramaters. I guess using the DTO object more closely represents what you're sending in the AJAX call (a JSON object).

asp.net MVC 3 - reading POST payload in paramterized controller method

I had
[HttpPost]
public ActionResult Foo()
{
// read HTTP payload
var reqMemStream = new MemoryStream(HttpContext.Request.BinaryRead(HttpContext.Request.ContentLength));
....
}
The payload is application/json; worked fine; then I changed to
public ActionResult Foo(string thing)
{
....
}
The intention being to post to MyController/Foo?thing=yo
Now I cant read the payload(the length is correct but the stream is empty). My guess is that the controller plumbing has eaten the payload looking for form post data that can be mapped to the method parameters. Is there some way that I can stop this behavior (surely MVC should not have eaten a payload whose type is marked as JSON , it should only look at form post data). My work around is to add 'thing' to the json but I dont really like that
Try resetting the input stream position before reading:
public ActionResult Foo(string thing)
{
Request.InputStream.Position = 0;
var reqMemStream = new MemoryStream(HttpContext.Request.BinaryRead(HttpContext.Request.ContentLength));
....
}
Now this being said, if you are sending an application/json payload why on the holy Earth are you bothering to read directly the request stream instead of simply defining and using a view model:
public class MyViewModel
{
public string Thing { get; set; }
public string Foo { get; set; }
public string Bar { get; set; }
...
}
and then:
public ActionResult Foo(MyViewModel model)
{
// use the model here
....
}
ASP.NET MVC 3 has a built-in JsonValueProviderFactory which allows you to automatically bind JSON requests to models. And if you are using an older version it is trivially easy to add such factory your self as Phil Haack illustrates in his blog post.

Knockout.js & list of checkboxes: post to mvc controller

I have an MVC view model that looks like this:
public class DirectorySearchModel
{
[Display(Name = "First name contains")]
public string FirstName { get; set; }
[Display(Name = "Last name contains")]
public string LastName { get; set; }
public CountriesCollection Countries { get; set; }
public IEnumerable<Country> SelectedCountries { get; set; }
public IEnumerable<Country> AllCountries { get; set; }
}
The CountriesCollection object (line 9) looks like this:
public class CountriesCollection
{
[Display(Name = "Countries")]
public int[] arrCountries { get; set; }
}
Now, I'm creating a new, blank instance of CountriesCollection and then adding it to a blank instance of the DirectorySearchModel view model and then serialising it all into a javascript view model for Knockout.js:
{
"FirstName":null,
"LastName":null,
"Countries":{"arrCountries":[]},
"SelectedCountries":[{"RelationshipManager":{},"CountryId":1,"CountryName":"UK"},{"RelationshipManager":{},"CountryId":2,"CountryName":"France"},{"RelationshipManager":{},"CountryId":3,"CountryName":"Spain"}],
"AllCountries":[{"RelationshipManager":{},"CountryId":1,"CountryName":"UK"},{"RelationshipManager":{},"CountryId":2,"CountryName":"France"},{"RelationshipManager":{},"CountryId":3,"CountryName":"Spain"}]
}
My checkboxes are rendered as: <input checked="checked" data-bind="checked: Countries.arrCountries" id="Countries_arrCountries30" name="Countries.arrCountries" type="checkbox" value="1">. Checking a couple means you end up with this Knockout.js view model:
{
"FirstName":null,
"LastName":null,
"Countries":{"arrCountries":["1", "3"]},
"SelectedCountries":[{"RelationshipManager":{},"CountryId":1,"CountryName":"UK"},{"RelationshipManager":{},"CountryId":2,"CountryName":"France"},{"RelationshipManager":{},"CountryId":3,"CountryName":"Spain"}],
"AllCountries":[{"RelationshipManager":{},"CountryId":1,"CountryName":"UK"},{"RelationshipManager":{},"CountryId":2,"CountryName":"France"},{"RelationshipManager":{},"CountryId":3,"CountryName":"Spain"}]
}
Submitting my view normally (i.e. via a submit button and not with Knockout.js) to an MVC action that expects a DirectorySearchModel, I'm able to ask for model.Countries.arrCountries to get a list of the checked items, but when I use...
$.post("/MyController/MyAction", ko.toJS(viewModel), function(returnData) {
$("#resultCount").html(returnData);
});
or...
$.post("/MyController/MyAction", viewModel, function(returnData) {
$("#resultCount").html(returnData);
});
to another action that expects the same DirectorySearchModel, model.Countries.arrCountries is always null! I wondered if it's due to Knockout.js posting the arrCountries entries as string[]s when MVC is expecting int[]s, but changing my MVC code to expect string[]s doesn't seem to change much..! The CountriesCollection object within the DirectorySearchModel appears to exist, but it's the arrCountries within that's always null.
Any ideas? Any help much appreciated!
Edit
The action that receives the Knockout.js viewModel:
public MvcHtmlString ResultCount(DirectorySearchModel model)
{
return new MvcHtmlString(getResultCount(model).ToString());
}
The getResultCount method:
public int getResultCount(DirectorySearchModel model)
{
IUserRepository userRepository = new UserRepository();
int count = userRepository.Search(model, null).Count();
return count;
}
FIXED!
Thanks to Konstantin for pointing out that a simple switch from $.post to $.ajax to send my Knockout.js view model back to my mvc action was all that was needed! Here's the $.ajax code I'm using:
$.ajax({
type: "POST",
url: "/MyController/MyAction",
data: ko.toJSON(viewModel),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
$("#resultCount").html(data);
}
});
You cant use $.post you need to go for the underlying $.ajax and add the correct contenttype to make mvc accept the posted json and do the model binding (contenttype should be "application/json; charset=utf-8") google for it and you will se lots of examples

Knockout and MVC3: Posting JSON to action, serializing twice? Can't convert to C# objects?

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?
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.
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);
}
});
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
#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 });
}

Resources