I have an Web Api application with simple controller. Get methods work fine, but i have an issue with post and put requests.
[Route("api/[controller]")]
[EnableCors("AllowAll")]
public class LessonController : Controller {
...
[HttpPut("{id}")]
public void Put(int id, [FromBody] Lesson lesson) {
...
}
....
}
where Lesson is
public class Lesson {
public int Id { get; set; }
public string Name { get; set; }
public string Text { get; set; }
public string Description { get; set; }
public bool IsModerated { get; set; }
public int? PrevLessonId { get; set; }
public int? NextLessonId { get; set; }
}
So i try to send request and have no luck, lesson is just an object with default-initialized properties. I sent request in two way: first with js
$.ajax({
type: "POST",
url: 'http://localhost:1822/api/lesson/1',
data: JSON.stringify({
lesson: {
description: "Fourth lesson description",
isModerated: true,
name: "Fourth lesson",
nextLessonId: 5,
prevLessonId: 3,
text: "Fourth lesson text"
}}),
contentType: "application/json",
success: function (data) {
alert(data);
}
});
and with Postman:
So the content type is correct. Can anyone tell me what problem is connected with?
UPD:
I have tried to use PostLesson model, that contains all properties from Lesson but Id and sent request via Postman with UpperCamelCase data in body, but it does not solve my problem.
I have solved my own issue. In fact the problem is pretty simple.
We just need to pass object in Post method that equals of structure of Lesson model without specify argument name. So my js code need looks like
$.ajax({
type: "POST",
url: 'http://localhost:1822/api/lesson/1',
data: JSON.stringify({
description: "Fourth lesson description",
isModerated: true,
name: "Fourth lesson",
nextLessonId: 5,
prevLessonId: 3,
text: "Fourth lesson text"
}),
contentType: "application/json",
success: function (data) {
alert(data);
}
});
For some addition information see this link.
Related
I'm having trouble creating a model that will bind to the form data below . The main difficulty I'm having is creating a model that will bind the filter (ie: the multi-dimensional array).
This is what I have so far. Can anyone help me to get this model to bind properly?
public class GetPagedRequest
{
public int Start { set; get; }
public int Limit { set; get; }
public string dir { set; get; }
public List<FilterRequest> filter { set; get; } //This won't bind properly
}
public class FilterRequest
{
public string field { set; get; }
public DataFilterRequest data { set; get; }
}
public class DataFilterRequest
{
public string type { set; get; }
public string value { set; get; }
}
I do not support the solution from jherrera:
[HttpPost]
public ActionResult ReceivePostFromJson()
{
string jsonStringify = new System.IO.StreamReader(Request.InputStream).ReadToEnd();
var obj = new JavaScriptSerializer().Deserialize<GetPagedRequest>(jsonStringify);
return View(obj);
}
if you want to recieve json data from the client on server side you should prepare your client side object to have the save structure like the server side viewmodel. That way the model binder will do its job perfectly when the form is submitted.
code not tested:
public ActionResult DeleteXXX(GetPagedRequest pagedRequestViewModel)
{
// Use AutoMapper or a UI Service to reshape/map your viewmodel to your domain model
return EmptyResult();
}
Update
After reading the capabilities of DefaultModelBinder, I saw the problem in the post data.
Before anything, just change the class of the filter property to:
public IList<FilterRequest> filter { set; get; } //(IList<T> instead of List<T>)
Here is a query string that the DefaultModelBinder will Bind correctly:
start=0&limit=5&dir=ASC&sort=noSort&filter[0].field=CompanyName&filter[0].data.type=string&filter[0].data.value=Telus&filter[1].field=VendorName&filter[1].data.type=string&filter[1].data.value=testtest
But js is generating this query string:
start=0&limit=5&dir=ASC&sort=noSort&filter[0][field]=CompanyName&filter[0][data][type]=string&filter[0][data][value]=Telus&filter[1][field]=VendorName&filter[1][data][type]=string&filter[1][data][value]=testtest
Now you can implement your own ModelBinder class, but I think is easier to serialize the object on the client side, just make sure that the content type is properly set. Here is an example made in jquery:
<script type="text/javascript">
$(function () {
$("#btnSendPost").click(function () {
var filterData = {
"start": 0, "limit": 5,
"dir": "ASC", "sort": "noSort",
"filter": [{ "field": "CompanyName", "data": { "type": "string", "value": "Telus" } },
{ "field": "VendorName", "data": { "type": "string", "value": "testtest" } }]};
$.ajax({
type: "POST",
url: "/Home/ReceivePostFromJson",
data: JSON.stringify(filterData),
dataType: "json",
contentType: 'application/json; charset=utf-8'
});
});
});
</script>
Then you only add this line to your Application_Start method in global.asax file:
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
That way the framework will bind correctly the model, and in the action method you'll receive the object
[HttpPost]
public ActionResult ReceivePostFromJson(GetPagedRequest g)
{
Original Answer
If you could Stringify the json from the control, before posting the filter data, then you could use JavaScriptSerializer to Deserialize the object.
This example is made in jquery:
<script type="text/javascript">
$(function () {
$("#btnSendPost").click(function () {
var filterData = {
"start": 0, "limit": 5,
"dir": "ASC", "sort": "VendorName",
"filter": [{ "field": "CompanyName", "data": { "type": "string", "value": "Telus" } },
{ "field": "VendorName", "data": { "type": "string", "value": "testtest" } }]};
$.ajax({
type: "POST",
url: "/Home/ReceivePostFromJson",
data: JSON.stringify(filterData),
dataType: "application/json"
});
});
});
</script>
Then in the controller:
[HttpPost]
public ActionResult ReceivePostFromJson()
{
string jsonStringify = new System.IO.StreamReader(Request.InputStream).ReadToEnd();
var obj = new JavaScriptSerializer().Deserialize<GetPagedRequest>(jsonStringify);
return View(obj);
}
That way you have the nested objects:
Note: In this example if you remove the function JSON.stringify (data: filterData,), you'll see on the controller side the query string that produce your problem (filter[0][field]: CompanyName). I don't know if there's a way to recover the json, if it's possible then you don't have to modify the grid control:
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
I have 3 classes with below definition
public class UserRole
{
public User User { get; set; }
public IEnumerable<Role> Role { get; set; }
}
public class Role
{
public int Id{ get; set; }
public string RoleName{ get; set; }
}
public class User
{
public int Id{ get; set; }
public string UserName{ get; set; }
}
This is the action method:
// GET: /Admin/UserManagement/Create
[HttpPost]
[Transaction]
public ActionResult Save(UserRole userrole)
{
}
Is there a way in Jquery to serialize some JavaScript vars into a class Obj and then send it to an MVC controller action via an AJAX post?
You could send a JSON request:
var data = {
user: {
id: 5,
userName: 'john'
},
role : [
{ id: 1, roleName: 'role 1' },
{ id: 2, roleName: 'role 2' },
{ id: 3, roleName: 'role 3' }
]
};
$.ajax({
url: '#Url.Action("Save")',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(data),
success: function(result) {
alert('success');
}
});
The JSON.stringify method is natively built into modern browsers but if you need to support legacy browsers you could include the json2.js script.
The easiest way to do it out-of-the box with all the parts happy (jQuery, MVC Framework) is to serialize the object graph as JSON data on the client and pass that into the POST. The MVC Framework is actually quite good at sorting the model binding out for you.
There is a downloadable sample with a similar complex object here:
http://theycallmemrjames.blogspot.ca/2010/05/aspnet-mvc-and-jquery-part-4-advanced.html
It deals with model binding in a number of different scenarios. Check out the suitcase example for your needs. Disclaimer: It's my blog, hope that doesn't offend.
Cheers.
I am trying to send AJAX POST request to an MVC Application
$.ajax({
type: 'POST',
dataType: 'json',
data: {"FirstName":"chris","LastName":"cane"},
contentType: 'application/json',
url: "http://dev.irp.com/irp.Ajax.Search/home/Foo",
success: function (data) {
alert(data);
}
});
This script is present on a different server on an ASP.NET application. My MVC App to handle the code is as below
[HttpPost]
public JsonResult Foo(fromclient test)
{
var obj = new SearchMemberServiceClient();
var members = obj.FindMember(test.FirstName, test.LastName, "", "", "", "").Members;
IEnumerable<Bar> sorted =
from a in members
orderby a.FirstName ascending
group a by new
{
a.FormattedFullName,
a.MembershipsProxy[0].GoodFromDate,
a.MembershipsProxy[0].GoodThroughDate,
} into k
select new Bar
{
FormattedName = k.Key.FormattedFullName,
goodfrom = k.Key.GoodFromDate,
goodthru = k.Key.GoodThroughDate,
};
return Json(sorted.ToList());
}
public class Bar
{
public string FormattedName { get; set; }
public DateTime goodfrom { get; set; }
public DateTime goodthru { get; set; }
}
public class fromclient
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
The problem is the script needs to post to that url and get the json data. But as the controller does not have any view, When I look inside the console on the client side it says 404 error for the url and also it says XMLHttpRequest cannot load http://dev.irp.com/irp.Ajax.Search/home/Foo. Origin http://web-dev.irps.com is not allowed by Access-Control-Allow-Origin.
I dont know if the problem has to do with the absolute path of the url for ajax request. If so how could I overcome this?
Due to the same origin policy restriction that't built into browsers you cannot send AJAX requests to different domains. A possible workaround is to have the server return JSONP instead of JSON. Here's an example of a custom JsonpResult that you could use in your controller action.
Can U try JSONP ? Why json? It's perfect to cross-domain.
I've seen other posts on this subject and have fiddled with variations but still cannot not get the JSON model binding to work correctly.
I have the following in my global.asax.cs Application_Start method:
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
The post back data looks like this:
{"UserName":"Mike","Password":"password","Persist":true}
My PoCo:
public class UserLoginViewModel {
public string UserName { get; set; }
public string Password { get; set; }
public bool Persist { get; set; }
}
The controller method fires properly but has default UserLoginViewModel object with UserName = null, Password = null, and Persist = false; the signature looks like this:
[HttpPost]
public ActionResult Logon(UserLoginViewModel model) {
if (ModelState.IsValid) {
...
The problem is on the client side! I didn't have the contentType set.
$.ajax({
url: location.href,
type: "POST",
data: ko.toJSON(this),
datatype: "json",
**contentType: "application/json charset=utf-8",**
success: function (data) { alert("success"); },
error: function (data) { alert("error"); }
});