How does ASP.NET MVC caching work for an AJAX request? - asp.net-mvc

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.

Related

jQuery axax lifecycle and response in ASP.NET MVC application or API in general

I need a clarification on jQuery Ajax lifecycle when calling an endpoint, in my specific case an ASP.NET MVC (Net core/Net 5) application.
What I specifically need is to know how the success/error event chain works and when exactly the control is returned to jquery once the http call is fired.
Let's imagine a jquery ajax like this
postData = JSON.stringify({test: 1, anotherVariable : "YEEEE"});
$.ajax({
url: '#Url.Action("MyAspNetAction", "MyAspNetController")',
type: "POST",
contentType: 'application/json; charset=utf-8',
dataType: "json",
data: postData,
success: function (result) {
showMessage(result.responseText, result.success ? 'success' : 'error', 3000)
},
error: function (error) {
showMessage(result.responseText, 'success', 3000)
}
});
On the server I have an async Task controller which we could define as:
public async Task<JsonResult> MyAspNetAction([FromBody] MyAspNetActionModel){
try{
await someLongAction();
return some jsonresult { success = true, responseText = "HELLO"}
}
catch(Exception ex)
{
return some jsonresult { success = false, responseText = "ERROR"}
}
}
which gets executed async from asp net (I guess).
Now, Let's suppose I do various risky task in this action, like manipulating
HttpContext.Session["key"]
ViewData["Key"]
calls to other async stuff which takes some time to get executed (let's say 30 seconds for the sake of putting up a worst-case scenario.
All this togheter.
Now,
WHEN javascript gets control back and WHEN SUCCESS???
Am I handling this right?
I mean, all that async stuff is happening (ajax async, mvc action is async and so on.
Will the jquery action go to succes only after my MVC action is completed and gets the result?
Or the mere fact that jquery ajax managed to call my action will result in a HTTP 200 (so it's ok/success) and it will ignore all the code called inside, which has to be handled in another way, and the following javascript will be executed?
Basically I want to know if I can execute all the code in the MVC action being sure that ajax will enter in succes/error only when the action completed all the code
AND
if the javascript following the jquery ajax will continue executing and the control to the ajax action will return later
The client and the server on an HTTP request work independently.
The client issues a request.
The server handles the request and sends the response.
The client receives the response.
Each end can be running on any architecture and, if possible, the client can block during 2 or asynchronously await for 3 during 2.
The server (2) can be synchronous or asynchronous, but it sequentially does:
Receive the request.
Handle the request.
Send the response.
Regarding ASP.NET, you should not be concurrently messing with HTTP-related components or view components.
Your action method should:
Collect data from HTTP (model binding and other data).
Call any model/services needed to handle the request.
Produce the response (View, or any other response type).

.NET MVC Cache, Chrome, Actions with Variable Results, and the HTML5 history API

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).

mvc async await link clicks wait till task finished

I have a MVC website where I am trying to call a method with async. My code is like below:
Views
Submit Download
$(document).on('click', '.submit-download', function (evt) {
var submit_url = '/SubmitDownloads/24589;
$.ajax({
type: 'POST',
url: submit_url,
success: function (data) {
if (data.Item1)
location.reload();
}
});
});
Controller
[HttpPost]
public async Task<JsonResult> SubmitDownloads(int id)
{
var respository = new WorkflowRepository();
var result = await respository.SubmitAsync(id);
return Json(result, JsonRequestBehavior.AllowGet);
}
Repository Method
//db service call which will take much longer time
public async Task<Tuple<bool, string>> SubmitAsync(id)
{
//long running method here
await Task.Delay(20000);
return new Tuple<bool, string>(true, "done with " + id);
}
When user clicks on the 'Submit Download' link in Views, it complete the entire function quickly as its supposed to do and page shows responsive like scrollable, menu shows fine. But when I click on any link in the page, it waits till the entire operation is finished (20 seconds) and then redirect to respective URL.
If I change the Task.Delay to 50 seconds, link click takes 50 seconds to redirect.
Can you please guide me what I am missing here?
But when I click on any link in the page, it waits till the entire operation is finished (20 seconds) and then redirect to respective URL.
Asynchronous controller methods don't make the HTTP interaction async, just the interaction from the web server to the web application async. The idea is that a high volume web server can free up threads to service other requests while long-running requests are doing their thing.
When you click on a link, the browser needs to wait for a response before, well, processing that response (displaying the page). There's no way to display a page without waiting for that page to be sent from the web server.

return Json will redirects to another view when Url specified

When you do return Json(...) you are specifically telling MVC not to use a view, and to serve serialized JSON data. Your browser opens a download dialog because it doesn't know what to do with this data.
i got the above information from below link
How to return Json object from MVC controller to view
when i give this below code in some action result
return Json(new { Url = "sep/employee"}
whether it will redirect to specified action ? how it redirects to the URl ?
for this case why i cant use return RedirectToAction("sep/employee").
how return Json code redirects to action which specified in the Url.
ex:
public ActionResult Index()
{
................................
return Json(new { Url = "sep/employee"}
}
public ActionResult employee()
{
....................
}
what is the difference b/s redirectaction and return Json
You return the following line to an ajax success call
return Json(new { Url = "sep/employee"});
you then need to specify where to redirect to the new page
success: function(result) {
window.location.href=result.Url;
}
RedirectToAction simply returns 302 code to your browser with URL telling where the browser should redirect to. The browser immediately makes yet another call to the server to that URL obtained from redirection response.
RedirectToAction internals are:
Construct redirection url from parameters passed to RedirectToAction
Return new RedirectToRouteResult
In ExecuteResult of RedirectToRouteResult you can find the following line:
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
which is merely returning 302 with redirection URL. More info - look at source code here.
Returning JSON data simply returns JSON serialized object to your browser. Is not meant to do any redirect. To consume such a result you will likely call the server using $.ajax:
$.ajax({
url: 'sep/employee',
type: 'POST'
success: function(result) {
// handle the result from the server, i.e. process returned JSON data
}
});
ExecuteResult of JsonResult just serializes passed CLR object to the response stream, which lands in your browser after response is fully received. Then you can handle such response in JavaScript code.
EDIT:
You of course can mimic 302 redirection by returning Json like
return Json(new { Url = "redirectionUrl"}
and at client side handle it like
$.ajax({
url: 'sep/employee',
type: 'POST'
success: function(result) {
// mimic the 302 redirection
windows.location.href = result.Url
}
});
although IMHO it should be avoided since you reinvent MVC infrastructure and enlarge your code base.
whether it will redirect to specified action ? how it redirects to the URl ?
I assume you mean to ask, "will it redirect to specified action? how will it redirect the the URI?"
To answer your question: How it redirects to the URL?
In your example it will redirect to the URL, if you made the HTTP request as AJAX and that you will explicitly handle the resulting HTTP response to actually redirect to the URL that you received. So your view should have something like this:
$.ajax({
url: '{your controller}/index',
type: 'GET'
success: function(url) {
// write code to redirect to the URL
// example:
// window.navigate(url)
}
});
If your view does not have anything that, then it will not redirect. You did not post your view, so I am assuming it just a normal page load.
Your next question, what is the difference b/s redirection and return Json?
If you really just want to redirect then do RedirectToAction from your controller, that is exactly what it is for. You can do the same effect using AJAX and JSON to be able to redirect, but AJAX and JSON serves a much wider purpose than just redirecting and in most cases (unless you have very good reasons) you probably will not want replace RedirectToAction with that approach.

Loading Page for ASP.Net MVC

I'm using ASP.Net MVC to create a web site which needs to do some processing (5 - 10 seconds) before it can return a view to the user. Rather than leaving the user staring at the glacial progress bar I'd like to show some sort of "Please Wait/We'll be right back" animated gif to keep them interested.
Does anyone know a good approach to achieving this?
(I found this answer but its not quite what I need, this uses jQuery to fetch data once the view has been returned. I'd like to display the "Please Wait" while they're waiting for the view to appear)
Thanks
I think the solution you referenced will work for you. You just need to have your initial controller action return right away with the "please wait message", then have the AJAX call do the actual retrieval of the contents based on your processing. If the request really takes 5-10 seconds you may also need to adjust the timeout value on the AJAX request so that it is able to complete. I don't know what the default timeout is but is may be less than what you need.
EDIT Example:
View code:
<script type="text/javascript">
$(document).ready( function() {
$.ajax({
type: "POST",
url: '<$= Url.Action("GetSlowData","Controller") %>',
data: 'id=<%= ViewData["modelID"] %>',
timeout: 15000, // wait upto 15 secs
success: function(content){
$("#container").html(content);
}
});
});
</script>
...
<div id="#container">
Please wait while I retrieve the data.
</div>
Controller
public ActionResult ViewMyData( int id )
{
ViewData["modelID"] = id;
return View();
}
[AcceptVerbs( HttpVerbs.Post )]
public ActionResult GetSlowData( int id )
{
var model = ... do what you need to do to get the model...
return PartialView(model);
}
You'll also need a partial view (ViewUserControl) that takes your model and renders the view of the model. Note that this isn't complete -- you'll need to add error handling, you may want to consider what happens if javascript isn't enabled, ...

Resources