MVC4 not binding a list of basic types - asp.net-mvc

I cannot, for the life of me, get this data to bind. Here's my JavaScript:
var params = { 'InvItemIDs': ["188475", "188490"]};
$.post("api/Orders/OrderFromInventory?" + $.param(params))
and the Controller action:
public HttpResponseMessage OrderFromInventory(IList<int> InvItemIDs)
{
return new HttpResponseMessage();
}
I've built the query string so that it's sending:
?InvItemIDs=188475&InvItemIDs=188490
as well as
?InvItemIDs[]=188475&InvItemIDs[]=188490
and even
?InvItemIDs[0]=188475&InvItemIDs[1]=188490
and none of them are binding. InvItemIDs is always null. What am I doing wrong?
EDIT:
So it turns out all this is a bug (or something) in the new Web API controller code in MVC4. As soon as I moved the exact same code over to a standard controller it started working.
I'm still interested if anyone has any insight as to why the Web API would break this binding.

The reason it doesn't work is because you are using [HttpPost] where it will be expecting the data to be posted in "post body" instead of URL.
You can either
1, remove httpPost
2. put the list in the post content
You would need to set the "traditional: true" for the array to work. Here is a sample code that I've tested on my local project, give that a try
var InvItemIDs = ["188475", "188490"];
$.ajax({ type: "POST",
url: "Home/TestIndex",
datatype: "json",
traditional: true,
data:
{
'InvItemIDs': InvItemIDs
}
});

This Haacked blog post may help. Specifically, looking at his first example, what happens if you change IList to ICollection?
Something like this "should" work
[HttpGet]
public HttpResponseMessage OrderFromInventory(IList<int> InvItemIDs)
{
return new HttpResponseMessage();
}
with the querystring
?InvItemIDs=188475&InvItemIDs=188490

Related

WebAPI not found

Sadly, I cannot get the most basic of things working with WebAPI
$.ajax({
url: "https://192.168.1.100/Api/Authentication/LogIn",
type: "POST",
contentType: "application/json",
data: "{ 'username': 'admin', 'password': 'MyPass' }",
error: function (r, s, e) { alert(e); },
success: function (d, s, r) { alert(s); }
});
I get "Not found"
API controller definition
public class AuthenticationController : ApiController
{
[HttpPost]
public bool LogIn(string username, string password)
{
return true;
}
}
If I remove HttpPost and replace it with HttpGet and then do
$.ajax({
url: "https://192.168.1.100/Api/Authentication/LogIn?username=admin&password=MyPass",
type: "GET",
error: function (r, s, e) { alert(e); },
success: function (d, s, r) { alert(s); }
});
That works fine.
What's wrong with WebAPI?
This article should help answer some of your questions.
http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
I believe the thinking here is that, especially in a RESTful API, you’ll want to bind data to the single resource that a particular method deals with. So, pushing data into several loose parameters isn’t the sort of usage that Web API caters to.
When dealing with post data, you can tell your action method to bind its parameters correctly like this:
public class LoginDto {
public string Username { get; set; }
public string Password { get; set; }
}
[HttpPost]
public bool LogIn(LoginDto login) {
// authenticate, etc
return true;
}
A couple things. Yahia's change is valid. Also, POSTs need a little direction in WebAPI to know where to look for their data. It's pretty silly in my opinion. If you know it's a POST, look at the message body. At any rate, change your POST to this and things will work. The attribute tells WebAPI to look in the body and the model does binding.
The AuthModel is just a simple model containing your username and password properties. Because of the way WebApi wants to bind to the input, this will make your life easier.
Read here for more details:
http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-1
Should be good to go with those changes.
Binding in WebAPI doesn't work if you use more than 1 parameter.
Though the same works in MVC controller.
In WebAPI use a class to bind two or more parameters. Read useful article:
http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
You may solve it the following ways:
1. Do the same in MVC action (it works)
2. Stay parameterless Post and read request like this
[HttpPost]
[ActionName("login")]
public async Task<bool> Post()
{
var str= await Request.Content.ReadAsStringAsync();
//
3. Incapsulate parameters to class like gyus prompted
}
Hope it helps ;)
POST action can have only 1 body...
There is no way to send 2 bodies (in your case 2 strings).
Because of this, the WebAPI parser would expect to find it in URL and not in body.
You can solve it by putting attributes and set that one parameter will come from URL and another from body.
In general, When there is only one object parameter in method - there is no need for the attribute [FromBody].
Strings would be expected to be in URL.
So - you can try send them in the URL as parameters (much like you did in GET)
Or - build a class to wrap it.
I would strongly recommend to use POST for login action.

ASP.NET MVC Model binding doesn't work with AJAX GET but works with Post

I'm having a problem using the Jquery AJAX as a GET Request.
For some reason the ASP.NET MVC model binder doesn't seem to be able to bind to my filter item. What happens is the action result is called but an empty object is created.
However if I change from HTTP Get to HTTP Post then it works.
Why would that be?
From what I understand it would be better to use GET as no data is changing on the server.
Here's a stripped down version of my code:
AJAX:
$.ajax({
url: url,
contentType: 'application/json',
dataType: 'json',
type: "GET",
data: "{'filter':" + ko.toJSON(model.filter) + "}",
error: function (xhr, textStatus, errorThrown) {
},
success: function (returnedData) {
}
ActionResult:
[HttpGet]
public virtual ActionResult Index(IFilter filter)
{
ViewModel filteredViewModel = GetFilteredViewModel(filter);
if (Request.IsAjaxRequest())
{
return toJSON(filteredViewModel );
}
return View(filteredViewModel );
}
Filter:
public class Filter: IFilter
{
public Nullable<DateTime> LogDate { get; set; }
public Nullable<int> SpecificItem_ID { get; set; }
}
First, just to clear up misconceptions, POST doesn't mean change, necessarily. It's perfectly valid to request via POST when accessing a "function", for lack of a better word. For example:
# Request
POST /add-xy
{ "x": 2, "y": 2 }
# Response
200 OK
4
Nothing has "changed", but POST is still the most appropriate HTTP verb.
That said, there's a fundamental difference between GET and POST requests, namely the concept of a POST "body". A POST body can have a content type and therefore can be interpreted properly on the server-side as JSON, XML, etc. With GET, all you have is a querystring, which is just simply a string.
The problem you're having is that with GET, the filter "object" is just a string, and since a string does not implement IFilter the modelbinder can't bind it. However, via POST, the filter "object" is sent in the POST body with a proper content type. So, the modelbinder receives it as JSON, and maps the JSON object onto an implementation of IFilter.
The moral is that GET is only viable for simple requests -- with data that's pretty much only name-value pairs of simple types. If you need to transmit actual objects, you need to use POST.
I don't know why it was accepted, but the currently accepted answer is totally wrong.
ModelBinders don't bind the sent parameters if your object name is precisely filter. So change the name of the object and it will bind properly.

Help with Ajax post to action method

I am a new to MVC an need a little help.
In my view I make an ajax post as below.
function PostCheckedPdf(e) {
var values = new Array();
$('input:checked').each(function () { values.push(this.value); });
$.post("/UnregisteredUserPreview/DownloadPdfInvoice",
{ checkedList: values });
}
This post the values of any checkboxes that are checked inside a third party Grid component (Telerik). The Action method receives the array fine and loops through each value rendering a pdf report and putting the report into a ZipStream which is attached to the Response. After the loop the zipstream is closed and I return View();
When the Action is invoked through the $.post it runs through the action method but nothing happens in the browser.
If I call the Action through an action link (with a couple of hard coded value instead of passing the checked boxes values) the zip file with all the pdfs is downloaded.
What am I doing wrong or how can I post the checked values with an ActionLink?
Thanks in Advance!
Toby.
The difference is that your ActionLink is emitting an <a> tag, which is performing a GET operation. The browser interprets the contents of the response and opens the PDF.
Your jQuery method is performing a POST, but does nothing with the response, and thus silently throws it away in the background.
You need to actually do something with the return contents, like write it out to another window.
var w = window.open('', '', 'width=800,height=600,resizeable,scrollbars');
$.post("/UnregisteredUserPreview/DownloadPdfInvoice",
{ checkedList: values },
function(content){
w.document.write(content);
w.document.close(); // needed for chrome and safari
});
You are making an Ajax call to the server there and client side code should receive the returned result which seems that you are not doing there. It should be something like below :
$.ajax({
type: 'POST'
url: '/UnregisteredUserPreview/DownloadPdfInvoice',
data: { checkedList: values },
success: function (r) {
alert(r.result);
}
});
And assume that your controller is like below :
public ActionResult DownloadPdfInvoice() {
//do you stuff here
return Json(new { result = "url_of_your_created_pdf_might_be_the_return_result_here"});
}
NOTE
If you are posting your data with anchor tag, it is better to
prevent the default action of this tag so that it won't do anything
else but the thing you're telling it to do. You can do that by adding the
following code at the end of your click event function :
$("#myLink").click(function(e) {
//do the logic here
//ajax call, etc.
e.preventDefault();
});
Have a look at the below blog post as well. It might widen your thoughts :
http://www.tugberkugurlu.com/archive/working-with-jquery-ajax-api-on-asp-net-mvc-3-0-power-of-json-jquery-and-asp-net-mvc-partial-views

Replace partialview after action in ASP.Net MVC

I'm still quite new to ASP.NET MVC and wonder how-to achieve the following:
On a normal view as part of my master page, I create a varying number of partial views with a loop, each representing an item the user should be able to vote for. After clicking the vote-button, the rating shall be submitted to the database and afterwards, the particular partial view which the user clicked shall be replaced by the same view, with some visual properties changed. What is the best practice to achieve this?
Here's how I started:
1. I defined the partial view with an if-sentence, distinguishing between the visual appearance, depending on a flag in the particular viewmodel. Hence, if the flag is positive, voting controls are displayed, if it's negative, they're not.
I assigned a Url.Action(..) to the voting buttons which trigger a controller method. In this method, the new rating is added to the database.
In the controller method, I return the PartialView with the updated ViewModel. UNFORTUNATELY, the whole view get's replaced, not only the partial view.
Any suggestions how-to solve this particular problem or how-to achieve the whole thing would be highly appreciated.
Thanks very much,
Chris
Trivial (but by all means correct and usable) solution to your problem is Ajax.BeginForm() helper for voting. This way you change your voting to ajax calls, and you can easily specify, that the result returned by this call (from your voting action, which will return partial view with only 1 changed item) will be used to replace old content (for example one particular div containing old item before voting).
Update - 11/30/2016
For example:
#using (Ajax.BeginForm("SomeAction", "SomeController", new { someRouteParam = Model.Foo }, new AjaxOptions { UpdateTargetId = "SomeHtmlElementId", HttpMethod = "Post" }))
ASP.NET MVC is a perfect framework for this kind of needs. What I would do if I were in your possition is to work with JQuery Ajax API.
Following blog post should give you a hint on what you can do with PartialViews, JQuery and Ajax calls to the server :
http://www.tugberkugurlu.com/archive/working-with-jquery-ajax-api-on-asp-net-mvc-3-0-power-of-json-jquery-and-asp-net-mvc-partial-views
UPDATE
It has been asked to put a brief intro so here it is.
The following code is your action method :
[HttpPost]
public ActionResult toogleIsDone(int itemId) {
//Getting the item according to itemId param
var model = _entities.ToDoTBs.FirstOrDefault(x => x.ToDoItemID == itemId);
//toggling the IsDone property
model.IsDone = !model.IsDone;
//Making the change on the db and saving
ObjectStateEntry osmEntry = _entities.ObjectStateManager.GetObjectStateEntry(model);
osmEntry.ChangeState(EntityState.Modified);
_entities.SaveChanges();
var updatedModel = _entities.ToDoTBs;
//returning the new template as json result
return Json(new { data = this.RenderPartialViewToString("_ToDoDBListPartial", updatedModel) });
}
RenderPartialViewToString is an extension method for controller. You
need to use Nuget here to bring down a very small package called
TugberkUg.MVC which will have a Controller extension for us to convert
partial views to string inside the controller.
Then here is a brief info on how you can call it with JQuery :
var itemId = element.attr("data-tododb-itemid");
var d = "itemId=" + itemId;
var actionURL = '#Url.Action("toogleIsDone", "ToDo")';
$("#ajax-progress-dialog").dialog("open");
$.ajax({
type: "POST",
url: actionURL,
data: d,
success: function (r) {
$("#to-do-db-list-container").html(r.data);
},
complete: function () {
$("#ajax-progress-dialog").dialog("close");
$(".isDone").bind("click", function (event) {
toggleIsDone(event, $(this));
});
},
error: function (req, status, error) {
//do what you need to do here if an error occurs
$("#ajax-progress-dialog").dialog("close");
}
});
There needs to be some extra steps to be taken. So look at the blog post which has the complete walkthrough.

MVC User Controls + ViewData

Hi im new to MVC and I've fished around with no luck on how to build MVC User Controls that have ViewData returned to them. I was hoping someone would post a step by step solution on how to approach this problem. If you could make your solution very detailed that would help out greatly.
Sorry for being so discrete with my question, I would just like to clarify that what Im ultimatly trying to do is pass an id to a controller actionresult method and wanting to render it to a user control directly from the controller itself. Im unsure on how to begin with this approach and wondering if this is even possible. It will essentially in my mind look like this
public ActionResult RTest(int id){
RTestDataContext db = new RTestDataContext();
var table = db.GetTable<tRTest>();
var record = table.SingleOrDefault(m=> m.id = id);
return View("RTest", record);
}
and in my User Control I would like to render the objects of that record and thats my issue.
If I understand your question, you are trying to pass ViewData into the user control. A user control is essentially a partial view, so you would do this:
<% Html.RenderPartial("someUserControl.ascx", viewData); %>
Now in your usercontrol, ViewData will be whatever you passed in...
OK here it goes --
We use Json data
In the aspx page we have an ajax call that calls the controller. Look up the available option parameters for ajax calls.
url: This calls the function in the class.(obviously) Our class name is JobController, function name is updateJob and it takes no parameters. The url drops the controllerPortion from the classname. For example to call the updateJob function the url would be '/Job/UpdateJob/'.
var data = {x:1, y:2};
$.ajax({
data: data,
cache: false,
url: '/ClassName/functionName/parameter',
dataType: "json",
type: "post",
success: function(result) {
//do something
},
error: function(errorData) {
alert(errorData.responseText);
}
}
);
In the JobController Class:
public ActionResult UpdateJob(string id)
{
string x_Value_from_ajax = Request.Form["x"];
string y_Value_from_ajax = Request.Form["y"];
return Json(dataContextClass.UpdateJob(x_Value_from_ajax, y_Value_from_ajax));
}
We have a Global.asax.cs page that maps the ajax calls.
public class GlobalApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "EnterTime", action = "Index", id = "" } // Parameter defaults (EnterTime is our default controller class, index is our default function and it takes no parameters.)
);
}
}
I hope this gets you off to a good start.
Good luck
I am pretty sure view data is accessible inside user controls so long as you extend System.Web.Mvc.ViewUserControl and pass it in. I have a snippet of code:
<%Html.RenderPartial("~/UserControls/CategoryChooser.ascx", ViewData);%>
and from within my CategoryChooser ViewData is accessible.
Not sure if I understand your problem completely, but here's my answer to "How to add a User Control to your ASP.NET MVC Project".
In Visual Studio 2008, you can choose Add Item. In the categories at the left side, you can choose Visual C# > Web > MVC. There's an option MVC View User Control. Select it, choose a name, select the desired master page and you're good to go.

Resources