.NET MVC Cache, Chrome, Actions with Variable Results, and the HTML5 history API - asp.net-mvc

I have a few actions in my .NET MVC Controller that will return a full view on a regular request, but only a partial if the request is an Ajax request.
At the end of the controller action...
if (HttpContext.Request.IsAjaxRequest())
{
return PartialView("_Partial", model);
}
else
{
return View(model);
}
On the same controller, I have set some caching options so that one result should be cached for ajax requests, and one for regular requests.
[OutputCache(Duration = 120, VaryByParam = "*", VaryByHeader="X-Requested-With")]
Finally, I'm controlling the history using history.pushState and the onpopstate event so that if the user clicks the "back" button, only the part of the page that would change gets reloaded.
jQuery(document).ready(function () {
if (history.pushState) {
$(window).on("popstate", function (e) {
FunctionThatUsesAjaxToRewriteThePage();
});
}
$('#SomeForm').on('submit', function (e) {
e.preventDefault();
var url = FunctionToGetUrlToUse();
if (history.pushState) {
history.pushState(null, null, url);
FunctionThatUsesAjaxToRewriteThePage();
}
else {
window.location.href = url;
}
});
});
This works fine, except in Google Chrome. Sometimes, when I navigate to the page by way of the browser's back and forward button, Chrome will inappropriately render the AJAX return value instead of the full page.
So far, this only seems to happen if I use the back/forward buttons to navigate to a different page (either another action on my MVC site, or another site on my domain) and then return to the offending action using the back or forward buttons. Detailed navigation steps:
I load up my site's home page (http://mysite.co).
I click a link on the home page to the controller action that uses all the Ajax/history API magic (http://mysite.co/month/2016/december).
I click a link within that page that triggers an ajax rewrite of the main page content (http://mysite.co/month/2016/january).
I click back (triggers a popstate event, URL changes to http://mysite.co/month/2016/december and AjaxRewrite function gets called).
I click back again (back to http://mysite.co).
I click forward. This returns me to http://mysite.co/month/2016/december, but instead of the full page, I just get the ajax version of the action, which is only a partial view.
Firefox and IE don't have this misbehavior.
Things I've tried to address this:
Specify the output cache location to be the server only. Even after clearing the cache in Chrome, I still get the AJAX version of a page when I ought not.
[OutputCache(Duration = 120, VaryByParam = "*", VaryByHeader="X-Requested-With", Location=OutputCacheLocation.Server)]
Giving more standard/serializable objects to history.pushState
history.pushState({}, '', url);
or
history.pushState({id: "foo"}, '', url);
Adding an arbitrary parameter to the ajax request, per HTML5 History API: JSON displayed when going "back" to another page, and then "forward" again.
$.ajax
({
url: url,
type: 'GET',
traditional: true,
contentType: 'application/json',
data: {
//all my normal URL parameters go here
ajax: "true"
},
//.....

Nevermind! Actually the third thing I tried (adding a dummy parameter to the AJAX requests) did work, but I needed to clear my cache in Chrome before I would see the result.
In short, do this:
$.ajax
({
//...
data: {
//...your data here
ajax: "true" //Add this line or one like it to differentiate AJAX requests
},
And then clear your cache in Chrome (Ctrl+Shift+Del for Windows or Shift+Command+Del on a Mac).

Related

Controller Action called inside button click event through load not invoked on second time click in ASP.NET MVC

Iam new to ASP.NET MVC .
My question is:
I am developing an application in which on "Show Log" button click ,I need to show a popup window on which a grid view that get poppulated with dynamic Contents.
I implemented the "Show Log" button in cshtml. And in its click event ,i mentioned a javascript function "OnShowLogClick".
Inside javascript function "OnShowLogClick"
I called my Controller Action(that returns the dynamic data to be binded inside the grid) through load method that is:
$(#popupWindowDiv).load("#Url.Action("Action", "Controller")", null,
function (response, status, xhr) {
if (status == "error") {
alert("An error occurred while loading");
}
else {
}
});
I have 2 Issues
1) On intial Click on "Show Log" button ,Controller action is invoked.but on second click javascript function is executed but controller action is not invoked.
2) Grid inside popupWindow is not poppulated with entire data model binded.Everytime only 9 entries are shown.
Please help me
Thanks in advance
This Problem was due to cache in browser. When you same request second time at the time it get the output from the cache it was not called sever request second time so it has two way to resolve the this problem
1) If you add attribute [OutputCache(NoStore = true, Duration = 0, VaryByParam = "")] above the action so cache will not maintain for that request.
2) And second way is you can add cache is false in ajax request
Example
$.ajax({
cache: false,
type: "POST",
url: "#(Url.Action("ActionName", "Controller"))",
data:Model,
success: function (data) {
},
error: displayerror
});

MVC routing changes url hash when requesting partial view via AJAX

I'm using AJAX calls to request partial views and load their html into a content area on my main Index view. I'm leveraging a hash in to the url to enable browser history support (the same way GMail url browser history works).
Everything is working fine, except after my partial view is returned and loaded, MVC seems to be clearing everything after my url hash symbol which affects the javascript browser history stack.
I have a link on my main view which initiates the request:
<div class="linkButton" data-bind="click:function(){Nav.makeRequest('#/MyController/Profile/2')}">Profile</div>
Here's the javascript that I'm using to request and load the partial views:
var Nav:function(){
var self = this;
$(window).bind("hashchange", self.onHashChange);
makeRequest: function(hash){
window.location.hash = hash;
};
onHashChange: function (e) {
var hash = window.location.hash.substring(1);
var url = 'http://localhost:3333/' + hash.substring(1);
$.get(url, function (data) {
$('#content').html(data);
});
}
}
So, one of my example requests would be for: http://localhost:3333/#/MyController/Profile/2
The request is completed successfully and my Profile view is loaded with the correct model for the id (2) passed to it in the routing and the url in the browser's navigation is what is shown above.
However after the view finishes loading, the browser's url then automatically changes to this: http://localhost:3333/#
This doesn't affect what's currently loaded on the page, but it adds this new url to the browser's history so when I hit the 'back' button it sends the request for the partial profile view again.
The only route I have in my Global.axax is the following:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}",
new { controller = "MyController", action = "Index", id = UrlParameter.Optional }
);
I suspect that the MVC routing engine sees my request for the partial view come in (http://localhost:3333/MyController/Profile/2) and then matches it to the Default route which returns the url for my Index view, which of course is: http://localhost:3333/
I have debugged extensively on the client and the onHashChange event does indeed fire both times, once for the partial view request and then again when the url changes back to localhost:3333/# The call stack doesn't reveal any calls being made client side to cause the url to change back.
Is there a way that I can request and load my partial view using AJAX, and hashes for history support, and have the browser's url not automatically route back to the default route path?
This must what you searching for:
To manipulation with browser history you need to use new method with Html5 support
//use it in ur ajax function to save history
history.pushState({page: 1}, "title 1", "?page=1");
//and to get ur history
window.onpopstate = function(event) {
something like
$.post('url',{page:event}function(event) {
do something
})
}

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

ASP.NET MVC multiple forms, staying on same page

I have forms located in multiple areas in my layout page (not nested).
I have a partial view which performs a post to controller action.
What action result do I return in that post to keep the user on the current page?
Is jquery/ajax my only option? I would rather a solution that didn't depend on javascript, maybe even a solution that degrades nicely.
You can use the Request.Referrer property to see what page the user has come from and then just use that to redirect them back there.
This does introduce other issues, e.g. losing ModelState, so you'll have to design for that. Also note that some users can block sending referrer information in their requests to the server - so the Referrer property can be null.
I would recommend using AJAX and then falling back on this.
You just need to do a RedirectToAction("") back to your main view.
To post a form without submitting the whole page, which refreshes the browser, you need to use Ajax/jQuery. The degraded solution is to submit the whole page like you would with a normal form.
Here's how I do it with jQuery.
Html:
<div id="RequestButtonDiv">
<button id="RequestButton" name="Request" type="button">Request</button>
</div>
This calls AddToCart on my Request controller when the RequestButton button is clicked. The response is placed inside the RequestButtonDiv element.
<script type="text/javascript">
$(document).ready(function () {
$('#RequestButton').click(function (event) {
$('#RequestButton').text('Processing...');
$('#RequestButton').attr('disabled', true);
submitRequest();
});
});
function submitRequest() {
$.ajax({
url: '<%: Url.Action("AddToCart", "Request", new { id = Model.RowId, randomId = new Random().Next(1, 999999) } ) %>',
success: function (response) {
// update status element
$('#RequestButtonDiv').html(response);
}
});
}
</script>
Controller action:
public ActionResult AddToCart(int id)
{
var user = AccountController.GetUserFromSession();
user.RequestCart.AddAsset(id);
return View("~/Views/Assets/Details_AddToCart.ascx");
}
The controller returns a partial view. You could also return Content("some stuff") instead.
Holler if you have questions or need more detail.

How does ASP.NET MVC caching work for an AJAX request?

I'm just starting to look in to caching to improve performance and have a question about caching for an AJAX call.
I have a action which is used to query twitter and then return the results. At the moment when a user presses a button it loads a rotating gif whilst it goes off to the action to do the query and then return a partial view. jQuery then updates a div with the HTML response from the view. Normally this takes around 5 secs. They then have a more button which goes off to get more results.
What will happen if I put the CachingAttribute over this action? I know I can try it but I just want the technical side of things explained.
Thanks
Here is my Javascript:
$('#blogEntryList #moreLink').live("click", function() {
$('#morespan').toggle();
$('#loader').toggle();
$.get($(this).attr("href"), function(response) {
$('#blogEntryList ol').append($("ol", response).html());
$('#blogEntryList #moreLink').replaceWith($("#moreLink", response));
$('#loader').hide();
$('#morespan').show();
});
return false;
});
Here is my modified Action:
[OutputCache(
Location = OutputCacheLocation.Server,
Duration = 100,
VaryByParam = "")]
public ActionResult BlogPosts(int? entryCount)
{
if (!entryCount.HasValue)
entryCount = defaultEntryCount;
int page = entryCount.Value / defaultEntryCount;
IEnumerable<BlogData> pagedEntries = GetLatestEntries(page, defaultEntryCount);
if (entryCount < totalItems)
AddMoreUrlToViewData(entryCount.Value);
return View("BlogEntries", pagedEntries);
}
Here's how it works: assuming no caching specified on the server side, by default GET requests will be cached by the browser and POST requests not cached unless you specify the cache: true attribute when sending the AJAX requests which allows you to override the client caching strategy.
Now on the server side you could decorate your controller action with the [OutputCache]
which will allow you to define different caching strategies. You could keep a cache on the server, on downstream proxy servers, or on the client. You could also manage different expiration policies.
So let's illustrate this by an example:
[OutputCache(
Location = OutputCacheLocation.Server,
Duration = 10,
VaryByParam = "")]
public ActionResult Hello()
{
return Content(DateTime.Now.ToLongTimeString(), "text/plain");
}
And on the client side:
$.ajax({
url: '/home/hello',
type: 'post',
success: function (result) {
alert(result);
}
});
The result of this controller action will be cached on the server for 10 seconds. This means that the server will be hit on each request but the action won't be executed if there's a cached version and will directly served from this cache. 10 seconds later from the first request which hit the controller action the cache will expire and the same process repeats.

Resources