The page I'm building depends heavily on AJAX. Basically, there is just one "page" and every data transfer is handled via AJAX. Since overoptimistic caching on the browser side leads to strange problems (data not reloaded), I have to perform all requests (also reads) using POST - that forces a reload.
Now I want to prevent the page against CSRF. With form submission, using Html.AntiForgeryToken() works neatly, but in AJAX-request, I guess I will have to append the token manually? Is there anything out-of-the box available?
My current attempt looks like this:
I'd love to reuse the existing magic. However, HtmlHelper.GetAntiForgeryTokenAndSetCookie is private and I don't want to hack around in MVC. The other option is to write an extension like
public static string PlainAntiForgeryToken(this HtmlHelper helper)
{
// extract the actual field value from the hidden input
return helper.AntiForgeryToken().DoSomeHackyStringActions();
}
which is somewhat hacky and leaves the bigger problem unsolved: How to verify that token? The default verification implementation is internal and hard-coded against using form fields. I tried to write a slightly modified ValidateAntiForgeryTokenAttribute, but it uses an AntiForgeryDataSerializer which is private and I really didn't want to copy that, too.
At this point it seems to be easier to come up with a homegrown solution, but that is really duplicate code.
Any suggestions how to do this the smart way? Am I missing something completely obvious?
You could use the conventional Html.AntiForgeryToken() helper to generate a hidden field somewhere on the page (not necessarily inside a form) and include it along the ajax request:
var token = $('input[name=__RequestVerificationToken]').val();
$.post(
'/SomeAction', { '__RequestVerificationToken': token },
function() {
alert('Account Deleted.');
}
);
To verify it on the server side:
[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult SomeAction()
{
return View();
}
If you have multiple tokens on your page you might need to specify which one to include. As the existing helper generates the hidden fields with the same names it is difficult to make a good selector so you could place them inside spans:
<span id="t1"><%= Html.AntiForgeryToken() %></span>
<span id="t2"><%= Html.AntiForgeryToken() %></span>
and then select the corresponding token:
var token = $('#t1 input[name=__RequestVerificationToken]').val();
Related
I am trying to build sth pretty simple, but I try to do it the correct way. But I struggle to figure out what is best.
I have a process chain where the user has to fill in some fields in different forms. Sometimes it depends from the user inputs which form the user is shown next.
[HttpGet]
public IActionResult Form1(Form1Vm f1vm)
{
return View(f1vm);
}
[HttpPost]
[ActionName("Form1")]
public IActionResult Form1Post(Form1Vm f1vm)
{
//process the data etc
//prepare the new viewmodel for the next form view (f2vm)
//Option1:
return View("Form2", f2vm);
//Option2:
return RedirectToAction("Form2", f2vm);
//for Option 2 I would need an additional HttpGet Action Method in which I
//would have to call Modelstate.Clear(); in order to not have the
//immediate validation errors on page load
//also all the properties of my viewmodel are passed as get parameters
//what looks pretty nasty for me
}
//More form views action methods should be added here...:
What is the better way? As mentioned in my comments above I have quite a big disadvantage for using the RedirectToAction option. However if I use the direct View(); call, I don't take care on https://en.wikipedia.org/wiki/Post/Redirect/Get and the user cannot simply refresh a page without getting a warning that his form is submitted once again.
Do I miss another way or don't see something obvious?
Edit: I just thought about a 3rd way, which I have seen quite often: Not transfering the whole VM to a HttpGet method but only the ID. I'd then have to load all the data stored previously directly from the db, map it again to my new VM and then call the View(); with this VM. Right now I think this is the "best" solution, however I feel like it is pretty laborious...
As per the dicussions, I would suggest using depending on your preference :
1) Save to db at the end of each form post and as you suggested use the I'd to redirect to a GET.
2) Depending on the the number of form pages and your requirements, retrieving values that a form needs on the get would be standard practice. This ensures that if a user drops off a form at any stage you can then start them off where they left off.
3) I wouldn't setup the viewmodel for the next form in the post of the previous. Generally as part of the single responsibility principle you want to ensure that your methods have only one reason to change.
4) PostRedirectGet pattern should be implemented with this to ensure data is not saved multiple times if a user refreshes after a post.
In my application there are sometimes in that I want to get a user request, save it in some place and then, in a next request, simulate saved request instead of real request, is that possible?
Use any web debugger. I use Fiddler 2.6.2 myself.
Find it here, https://www.telerik.com/download/fiddler
Be warned - there is no robust way to do it (there are a lot of edge cases to consider) but hopefully this will get you started:
Take Request.Form.ToString() and save it against the user somewhere. For testing I suggest putting it in a hidden field, but at some point you would probably need to move that to a database and associate that with the user somehow.
You will need to have a field that will tell you whether the previous request should be replayed. Using a query string parameter like ?replay=true will work. Then pull it into your page:
protected bool IsReplayRequest
{
get
{
return bool.Parse(this.GetValue(p => p.Request["replay"], "false"));
}
}
To simulate the request you will need to override the page's DeterminePostBackMode. This is one of the first methods ASP.NET calls to start the postback process. Use the saved request form converted into a NameValueCollection. Something like this:
protected override NameValueCollection DeterminePostBackMode()
{
if (IsReplayRequest && SavedFormData != null)
{
if (!string.IsNullOrWhiteSpace(SavedFormData.Data))
{
return HttpUtility.ParseQueryString(SavedFormData.Data ?? string.Empty);
}
}
return base.DeterminePostBackMode();
}
Where SavedFormData is just a class which holds the request form and the user information.
With those 3 steps, you should be able to succesfully simulate a previous request for a simple page
Now for the potential problems:
First, the viewstate might get in the way. If you have any listboxes or data grids or authentication etc, you will then need to save the viewstate with the form data. for that you will need to override the page GetStatePersister() method (Is it possible to make a custom implementation of ViewState?).
Second, once you have simulated a request, you need to make sure a) no data is corrupted and b) you remove the "replay" query string. Otherwise the request will play over and over again.
Third, depending on how you store the request form, you will have to think about how to associate it with the user/browser/session/window. Dealing with each one of those has it's own problems.
Once you've got everything above solved, you will successfully simulate a saved request!
I just came across this topic in a current project and I would like to know the best way to handle the "problem" of objects getting lost, when using the HttpPost method when submitting a form. Thoughout the web and the posts on stackoverflow I gathered some ways to deal with it.
So, once I got that a view model gets objects or lists from the HttpGet request they are lost because of the stateless nature of http. I now have seen different approaches to get that objects back when passing the view model back to the view.
Store objects in hidden field
#Html.HiddenFor(m => m.Object)
This seems to be working fine if the view models property is serializable. But what if there is logic behind the objects? Will this work for all scenarios? And on the other hand, I don't want every object inside my view model, just the ones for display and the user can interact with.
Get the object via AJAX and HelperMethods
#Html.Some3rdParty().ComboBoxFor(m => m.List)
.DataSourceUrl(#Url.Action("GetComboSource"))
[ComboBoxSource]
public ActionResult GetComboSource()
{
var data = Service.GetStaticSource();
return Json(data);
}
Using 3rd party libraries like Kendo UI MVC I can easily put a .DataSourceUrl() HtmlHelper on my control for requesting a controller to provide the datasource. Or just using jQuery's $.get() method. The problem is: What if I can't use such a library or the requirement says, that JS has to be avoided as much as possible?
Get the object back in the HttpPost Controller action
[HttpPost]
public ActionResult Edit(ViewModel viewModel)
{
var tempViewModel = Service.GetViewModel(viewModel.Id);
viewModel.Object = tempViewModel.Object;
return View(viewModel);
}
This one seems a bit brittle to me. On the post action I have to call whatever service provides the view model, extract certain objects and attach them to my recieved view model.
Currently that's all I can think of, but in short:
I'd like to know, what is the best practise to handle it throughout a project and what's the best way for maintainability?
For me it seems there is no perfect solution so far. How do you do it in your projects?
Avoid solution 1: you will weigh your requests, the data can be forged and if your object is not serializable (or the to string method does not provide useful string) you will have some problems. If you want to use something similar prefer Session
Solution 2: I use ajax populating when the input have to be filtered depending of other controls value. If it is not the case you will have controller with lot of actions and not easily maintanable.
Solution 3 is the one I use and I prefer because it is more DRY, more secure (no forgery), and keeps the controller light.
Just change
<input class="ui-input-text " type="text" id="PlateNo" tabindex="2" name="PlateNo">
to
<input class="ui-input-text " type="text" id="PlateNo" tabindex="2" name="PlateNo" value="#Request["PlateNo"]">
You can be show on the textbox sending value for this input.
Only add value="#Request["yourinputname"] to your input.
I have a question about using session, or maybe TempData to store data while working on one page. I store and retrieve data using multiple Ajax requests and I save to session on my controller Action. What I'd like to do is, when user leaves my page, I'd like to clear session variables. How would I do that with session? Or maybe there is some kind of other way to store data only for one http request (I am not sure about this because as I said I have multiple POST as well as GET requests within the page.)
Any help would be appreciated. Thanks
You dont you make use of Hiddenfields in your html to hold that data...
Same as Asp.Net using hiddenfield to store the viewstate data.....
Use Html.HiddenFor(myModel => myModel.SomeField) to do this. You can assign the values to it, and then it will automatically post back the values in to your model which is super handy dandy.
I hope this is helpful!
If you really want to use TempData (which I do on occasion), I have a KeepTempDataAlive(string tempKey) method that looks something like this...
if(TempData[tempKey] != null)
{
TempData.Keep(tempKey);
}
That should keep it around for 1 more post back. Just do this as long as you need it.
Depends on your intentions.
If you want to do async stuff with Ajax requests, you can store the data in a hidden field or the jQuery .data() on your .success event. Then when everything is done, use jQuery to populate what you need done.
If you want a true session variable (across multiple pages), then the best way to do that is an AJAX process on window.close to clear out session. Although that's limited to users who have javascript enabled. I don't think there's a foolproof way to do this.
I solved this problem utilizing jQuery and an ajax request that runs when the page unloads.
In your javascript:
$(window).unload(function () {
$.ajax({
url: applicationPath + "/api/ExampleApiController/ExampleWebMethod",
async: false,
type: 'GET',
data: {
"someVariable": anythingYouWantToPassIfAny,
"otherVariable": anythingElseYouWantToPass,
}
});
});
Note: You need to set async: false or else the page will unload before it has a chance to run the ajax request. For the type you can use either a GET or POST, whatever your preference is and what you all want to do with the request.
Then in your api controller:
public class ExampleApiController
{
[HttpGet]
public void ExampleWebMethod(string someVariable, string otherVariable)
{
HttpContext.Current.Session.Remove("sessionVariableA");
HttpContext.Current.Session.Remove("sessionVariableB");
...
}
}
You can also add code to check the return if you want to see if it was successful or not, but in my case I just wanted a quick thing to run and didn't really want it to return anything and it wasn't a big deal if it failed so I just send the request off and unload. I know this question is old, but hopefully it will help someone else.
Note: The problem with this solution is that if the user refreshes the page it will still call the unload event and drop all your session variables.
I am playing around with ASP.NET MVC for the first time, so I apologize in advance if this sounds academic.
I have created a simple content management system using ASP.NET MVC. The url to retrieve a list of content, in this case, announcements, looks like:
http://www.mydomain.com/announcements/list/10
This will return the top ten most recent announcements.
My questions are as follows:
Is it possible for any website to consume this service? Or would I also have to expose it using something like WCF?
What are some examples, of how to consume this service to display this data on another website? I'm primarily programming in the .NET world, but I'm thinking if I could consume the service using javascript, or do something with Json, it could really work for any technology.
I am looking to dynamically generate something like the following output:
<div class="announcement">
<h1>Title</h1>
<h2>Posted Date</h3>
<p>Teaser</p>
More
</div>
For now ... is it possible to return an Html representation and display it in a webpage? Is this possible using just Javascript?
There is nothing to stop another client just scraping that particular page and parsing through your HTML.
However you would probably want another view using the same controller that generates the data that doesnt contain excess formatting HTML etc. Maybe look at using a well known format such as RSS?
You can return the result as JSON using something like below:
public JsonResult GetResults()
{
return Json(new { message = "SUCCESS" });
}
I think I would offer a view which contains the items as xml and another that returns JSON that way you have the best of both worlds.
I have a small post about how to call and return something using MVC, JQuery and JSON here.
Your ROUTE is perfectly fine and consumable by anyone. The trick is to how you want to expose your data for that route. You said XML. sure. You can even do JSon or Html or just plain ole text.
The trick would be in your controller Method and the view result object.
Here's the list of main view results :-
ActionResult
ContentResult
EmptyResult
JsonResult
RedirectResult
eg.
public <ContentResult> AnnouncmentIndex(int numberOfAnnouncements)
{
// Generate your Xml dynamically.
string xml = "<div class=\"announcement\"><h1>Title</h1><h2>Posted Date</h3><p>Teaser</p>More</div>"
Response.ContentType = "application/xml"; // For extra bonus points!
return Content(xml);
}