I have a JQuery Ajax call that posts some data (Json) to a MVC Controller. Basically in my method I have
public JsonResult Save()
{
TryUpdateModel(MyModel)
...
}
What I want to do is change some values in the posted Ajax data, before calling TryUpdateModel(). I've tried numerous ways including
ViewData.ModelState.SetModelValue("MyTextBox", new
ValueProviderResult("Some string", string.Empty, new
System.Globalization.CultureInfo("en-US")));
Everything time it appears that the values I'm changing aren't being updated. Any ideas where I'm going wrong ?
Guess I'm still to new to post against your question, but what is your posted object? Model, string, etc. You left some detail out of the code.
Without more detail, this Posting JSON data via jQuery to ASP .NET MVC 4 controller action may help
Related
Long time lurker first time posting, so hey!
TL:DR - how would I keep a value from the original page-loading-controller to the ajax method without passing it in as a json object as part of the ajax request? And I can't use Session[]!
Backstory - I'm working on a filter-search functionality which brings back a big list of objects on the initial page load, generated from a database call and populates Model.FullResults.
In order to filter the results, rather than keep doing more database calls each time a filter is clicked, I'm simply filtering the results via linq in an AJAX method, like so:
var fullResults = #Html.Raw(Json.Encode(Model.FullResults));
var obj = {
//filter tickboxes statuses etc are here
fullResults: fullResults
}
var jqxhr = $.ajax({
//urls etc removed for clarity
data: JSON.stringify(obj),
success: function (response) {
//stuff
}
}
});
Which is all working perfectly fine for small values of Model.FullResults. However, if Model.FullResults is pretty large (i.e. on my main search page), this is a large amount of data to pass across via ajax each time, and the only reason I'm even passing it across this way is just for the plain and simple fact of I don't know how else I can "keep" this data from the original controller action and still have it for use the ajax method.
Storing Model.FullResults in Session was a brief thought, but there's a high probability people will open multiple tabs that hit the same controller, thus making session outdated. Something like Viewdata would be ideal but I can't access that in the ajax method of course.
So how would I keep this value for use in the ajax method? Do I have to resort to doing another database call OR passing it into the ajax method? There must be a way of retaining it somehow?
Thanks in advance you helpful bunch! :)
You can go for memoryCache to save large amount of data. Similer problem was with me where I have to read data from csv file and pass through Web API. Every-time, reading data from text was not a good idea, So I saved data in memory cache and fetching the records from memoryCache to avoid reading from stream. A simple demo is shown on my github account.you can co relate it.
https://github.com/abhee235/SimpleRestfulAPI/tree/master/DataProvider
Note : The project is not optimized for performance point of view. It is just a demo.
Alright. I can get the HTML I need from the page (it's all in a DIV). That's not the problem. What I need to do is take the HTML and pass it, via a C# class into a controller.
I tried doing something like this with knockout/jQuery:
var Details = $("#Details").html();
console.log(Details);
DetailsPdf.DetailsMarkup = JSON.stringify(Details);
var jsonData = ko.toJS(Details);
ko.utils.postJson("/MyController/MyAction", DetailsPdf);
The knockout actually DOES get me the relevant HTML. But when I pass it to my class, I get an exception that reads:
A potentially dangerous Request.Form value was detected from the client.
Then it partially shows the HTML I was sending as a part of the exception. I can't even seem to pass in the entities themselves without getting that exception.
This is an app with certain company-mandated security features, so turning off validation is not an option.
I need the HTML, or at least a way to re-create it on the server in the C#.
I'm still fairly new to knockout. Does anyone have any suggestions here?
You should have mentioned that:
This is an app with certain company-mandated security features, so
turning off validation is not an option.
In your recent question Using iTextSharp with the knockout JavaScript framework?. I could have provided this answer there.
I'm not sure why decorating the controllers Action with [ValidateInput(false)] isn't working, but that answer's fully working source code is available. Whatever the reason, you should only need a few changes to workaround the issue:
(1) Base64 encode the HTML in your JavaScript:
ko.utils.postJson("/MyController/MyAction", window.btoa(DetailsPdf));
(2) Decode string in your MVC Controller:
[HttpPost]
public ActionResult Index(string xHtml)
{
xHtml = Encoding.UTF8.GetString(Convert.FromBase64String(xHtml));
(3) Deserialize that string to your model / entities with Json.Net or other.
Above steps have also been tested and verified working. ;)
You should be able to decorate your model (the one that the controller action is expecting) with the [AllowHtml] attribute on the property that has the HTML.
When you do that, MVC skips the validation for that property.
Here's a link to the documentation for more information.
Note Use this ONLY when you need to. It does open a vector for XSS if misused.
Edit: If for some reason, You can't use the [AllowHtml] attribute, you can turn off validating the request for that one action with [ValidateInput(false)].
Same rules apply. Use that very sparingly. This means none of security validations will run against that particular model in that particular action only.
I have a form like ...
#using (Html.BeginForm("create", "Account", FormMethod.Post, new { id = "accountform_form" }))
{
#Html.TextBoxFor(e => e.ShipFirstName)
...
}
while testing, I was surprised to see the field retained its value on postback even without me assigning it to the view-model. Using the debugger, the value for ShipFirstName is null right at the end of the action when returning the view, so why would it show the value that was in the field? Have I been unnecessarily assigning posted values to view-model properties all this time? Or is there something else going on?
Update: the action is like so...
[HttpPost]
public ViewResult Create(AccountFormModel postModel)
{
var model = new AccountFormModel(postModel, stuff, stuff); //I use posted values and paramters to create the actual view model
return view(model);
}
So, I see the GET form, enter values, say I enter a field and leave a required field blank, submit, the resulting page has the value I entered in the other field, who's putting it there when in the model it's null?
I ran into something similar earlier today (a checkbox was always checked). Have a look at Changing ViewModel properties in POST action and see if this is similar.
Basically calling ModelState.Clear() fixed it for me.
As you're passing the model back to the view after it has been POSTed, MVC is taking the stance that you're doing so because the form contains errors. So, rather than making the user fill out the form again, it repopulates it using the ModelState collection. In this case, the values in the ModelState collection take precedence over the changes you make in the action (which does feel a bit weird).
You can get around this either by calling ModelState.Clear() or using ModelState.Remove(string key), where key is the name of the property.
If you'd like a full explanation of why this is the case, see ASP.NET MVC’s Html Helpers Render the Wrong Value!. Excerpt:
Why?
ASP.NET MVC assumes that if you’re rendering a View in response to an HTTP POST, and you’re using the Html Helpers, then you are most likely to be redisplaying a form that has failed validation. Therefore, the Html Helpers actually check in ModelState for the value to display in a field before they look in the Model. This enables them to redisplay erroneous data that was entered by the user, and a matching error message if needed.
Alright, I lost the entire morning on this and I'm getting nowhere.
I have a good experience with MVC, been using it since the first betas back in 2008 but I can' t really figure this out.
I substantially have 2 GET methods: the first one renders a form. The second one is the method the form points to. I'm submitting using GET because it's a search form, and I want to have a bookmarkable URL with the parameters.
Something like this
[HttpGet]
public ActionResult DisplayForm()
{
Contract.Ensures(Contract.Result<ActionResult>() != null);
Contract.Ensures(Contract.Result<ActionResult>() is ViewResult);
return this.View();
}
[HttpGet]
public ActionResult Search(MyViewModel viewModel)
{
Contract.Requires<ArgumentNullException>(viewModel != null);
Contract.Ensures(Contract.Result<ActionResult>() != null);
Contract.Ensures(Contract.Result<ActionResult>() is ViewResult || Contract.Result<ActionResult>() is RedirectToRouteResult);
var result = this.validator.Validate(viewModel); //FluentValidation validation
if (!result.IsValid)
{
result.FillModelState(this.ModelState); //extension method that uses AddModelError underneath for ValidationMessageFor helpers on search form
return this.RedirectToAction(c => c.DisplayForm()); //MvcContrib redirect to action
}
ViewData.Model = viewModel;
return View();
}
The first time I submit the form, the viewData in the target method gets populated correctly.
If I go back and do another search, it's like the modelbinder "cached" the data I submitted the first time: the viewData always has the values from the first search. It resets only restarting the application.
I tried checking both ModelState and HttpContext.Request and they DO have the new data in them (not stale) but still the viewData gets populated with old data. I also tried overriding OnActionExecuting and OnActionExecuted simply to put a breakpoint in there and check the ModelState in those steps, and found nothing weird.
I also tried to call the Search method directly via the browser bar, since it's in GET and I can do it. Still, ModelState and Request contain the data I input, but the viewData has old data.
This is really getting to my nerves as I can't really understand what's going on.
Any help would really be appreciated.
Have you tried
ModelState.Clear()
When you're done with the search call?
I experimented a lot and I found out that the problem was on an actionfilter on a base class which I was unaware of. The "PassParametersDurignRedirect" filter of MvcContrib. Without it everything works fine.
Completely new to asp.net mvc... completely new to web apps so bear with me...
Lets say I have an Action on a controller that requires a specific piece of information, say an int Id value.
A view is rendered from this Action. This view contains a button which will take the user off to a new Action on another Controller.
On the view of this second Action, there is a link that will send them back to the original Action on the Controller. Obviously, I've lost the original Id value and therefore the information I need to render my original view. What do I need to be looking into to solve this? Are there techniques/patterns that could be used to help?
I know I can keep passing the value around but if the other controller doesn't actually need this value it seems wasteful. I think I'm probably approaching the problem the wrong way to be honest. Any help appreciated.
I know I can keep passing the value
around but if the other controller
doesn't actually need this value it
seems wasteful.
But the second controller does need the id to function properly - as you've said, it needs the id to render the return link. Passing it around is the right thing to do.
There are many ways you can achieve this. Some of them are :
You can keep the value in the Query String.
If navigation from Action-1 to Action-2 is done via posting a form, you can send the value with a hidden input tag. Eg : <input type="hidden" name="n" value="v" />
You can use TempData to persist information between two requests (though it doesn't look like this is a good solution for your case)
Without knowing what exactly you are trying to do, it's hard to know best solution.