Laravel request()->ajax() triggering on browser back button - javascript

Inside my controller I have this function for the route /backups
public function index()
{
$backups = \App\Backup::all();
if(request()->ajax()) {
return $backups;
}
return view('backups.index', compact('backups'));
}
My idea was that if I have my javascript ask for the data then return json if not return html.
This works fine, except when pressing the browser back button to go from lets say /backups/1 to /backups it shows the json.
Is there another function I can use that will only respond to ajax calls from my code and not the browsers?

I'd recommend adding an ajax-only query string parameter to the ajax request, e.g. ?ajax=1.
This way, you can 1. utilise the browser cache, and 2. keep the same Laravel route for both request types.

Make sure your AJAX requests use a different URL from the full HTML documents. Chrome (and most probably Firefox) caches the most recent request even if it is just a partial.
Source:
https://code.google.com/p/chromium/issues/detail?id=108425
Or:
Try setting cache to false
$.ajax({
dataType: "json",
url: url,
cache: false,
success: function (data) {...}
});

Related

Using Antiforgerytoken in AJAX MVC

I was using antiforgerytoken within my razor view, and it was working fine. Then I moved the same code to AJAX, and it has started giving weird problems.
The AJAX POST request works fine, but the action SendData() which is called from the AJAX has a redirect to a different View (return View("Close");), which does not work anymore. It worked perfectly fine when the SendData() was called from the form directly.
Working code:
#using(Html.BeginForm("SendData", "Controller", FormMethod.Post)) {
#Html.AntiForgeryToken():
}
Not working code:
#using(Html.BeginForm("", "", FormMethod.Post, new
{
id = "__AjaxAntiForgeryForm"
}))
{
#Html.AntiForgeryToken()
}
JS File:
var form = $('#__AjaxAntiForgeryForm');
var token = $('input[name="__RequestVerificationToken"]', form).val();
$.ajax({
type: 'POST',
url: '/Controller/SendData',
data: {
__RequestVerificationToken: token
},
cache: false,
success: function(result) {}
});
The Action SendData() executes fine, but the redirect within this action doesn't work anymore.
I advise you use .serialize() instead of the following line.
var token = $('input[name="__RequestVerificationToken"]', form).val();
It is generic and thus will automatically adapt to any model changes.
For a working example of Antiforgery used alongside Ajax check this answer. The question was asked for MVC3 but it should work perfectly well as long as you're using an MVC version above 3.
The result param in the success: function (result) part of the ajax call will receive whatever was returned by your SendData action. If the SendData action uses a return View([...]); then you will probably get some html code in your result var. You could also return json data as follow return Json([...]).
If you actually want to have a redirect be made by the result of your Ajax call you should return the URL that you want to use as the redirection's target as follow return Content("http://target.url.for.redirect.com"); and then in your success callback, using the location.replace method, do this:
success: function(result)
{
document.location.replace(result);
}
This way the URL returned by the Ajax controller will be used to execute a redirection.
NOTE if you wish to ALWAYS redirect the user after the form is sent you should not use ajax at all ! A regular POST form would be far better suited for the job.
I figured out the problem, as per the comment from Stephen Muecke. The AJAX calls don't allow redirects, as the whole point behind AJAX is to stay on the same page and make a background JS call.

How to cancel MVC action if I use AJAX to call it?

I have an AJAX function on a view that calls an Action on one of my controllers to get a JSON object. The problem is that it takes a while to create the object. If the user reloads the page, then the AJAX call runs again. As far as I can tell the action is still running from the last request, but the response is no longer relevant because the new AJAX call is waiting for the new response.
My question is, how do I keep the action from continuing if the user leaves the view that called it?
Ajax:
$.ajax({
type: "get",
url: "/Alert/GetWallboardAlerts",
cache: false,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (result) { ... }
});
Controller action:
public ActionResult GetWallboardAlerts()
{
//...do long running stuff
//...still doing stuff when page reloaded or I navigate elsewhere
return Json(alertString, JsonRequestBehavior.AllowGet);
}
I don't think you can. Once you made a request to the server, you could close the browser but the request will still be executed.
This is just an idea:
1) Caller makes an AJAX request and provides a unique value (could be a guid).
2) Handle the page reload event and the close event and send a new AJAX request with the same unique key requesting a cancellation.
3) The long process task that is executed on the server should check for a flag that indicates that the task has been canceled.
You could also use a WebSocket for a task like this so the server could ping the client (like a "still alive" request). If you are using .NET you could use SignalR.
I hope it helps or at least it gives an idea to solve your problem.

Parallel JSONP requests in jQuery do not trigger multiple "callback events"?

I am experiencing an issue in jQuery when I do multiple jsonp requests, all with the same jsonpCallback function. It seems that only for the one of those the callback function is triggered. Are JSONP requests somehow overwriting each other?
Below an example of doing 2 jsonp request to github, and even though both firebug shows that both of them return, the callback function getName is only called for one of them:
function getName(response){
alert(response.data.name);
}
function userinfo(username){
$.ajax({
url: "https://api.github.com/users/" + username,
jsonpCallback: 'getName',
dataType: "jsonp"
});
}
users = ["torvalds", "twitter", "jquery"]
for(var i = 0; i < users.length; i++){
userinfo(users[i]);
}
Your request fired only once because of how jsonp works.
Jsonp means adding a script tag to the page from an outside domain to get around Cross-Site Scripting protections built into modern browsers (and now IE6 and 7 as of April 2011). In order to have that script interact with the rest of the script on the page, the script being loaded in needs to call a function on the page. That function has to exist in the global namespace, meaning there can only be one function by that name. In other words, without JQuery a single jsonp request would look like this:
<script>
function loadJson(json) {
// Read the json
}
</script>
<script src="//outsidedomain.com/something.js"></script>
Where something.js would look like this:
loadJson({name:'Joe'})
something.js in this case has a hard-coded callback to load the JSON it carries, and the page has a hard-coded loadJson function waiting for scripts like this one to load and call it.
Now suppose you want to be able to load json from multiple sources and tell when each finishes, or even load JSON from the same source multiple times, and be able to tell when each call finishes - even if one call is delayed so long it completes after a later call. This hard-coded approach isn't going to work anymore, for 2 reasons:
Every load of something.js calls the same loadJson() callback - you have no way of knowing which request goes with which reply.
Caching - once you load something.js once, the browser isn't going to ask the server for it again - it's going to just bring it back in from the cache, ruining your plan.
You can resolve both of these by telling the server to wrap the JSON differently each time, and the simple way is to pass that information in a querystring parameter like ?callback=loadJson12345. It's as though your page looked like this:
<script>
function loadJson1(json) {
// Read the json
}
function loadJson2(json) {
// Read the json
}
</script>
<script src="//outsidedomain.com/something.js?callback=loadJson1"></script>
<script src="//outsidedomain.com/somethingelse.js?callback=loadJson2"></script>
With JQuery, this is all abstracted for you to look like a normal call to $.ajax, meaning you're expecting the success function to fire. In order to ensure the right success function fires for each jsonp load, JQuery creates a long random callback function name in the global namespace like JQuery1233432432432432, passes that as the callback parameter in the querystring, then waits for the script to load. If everything works properly the script that loads calls the callback function JQuery requested, which in turn fires the success handler from the $.ajax call.
Note that "works properly" requires that the server-side reads the ?callback querystring parameter and includes that in the response, like ?callback=joe -> joe({.... If it's a static file or the server doesn't play this way, you likely need to treat the file as cacheable - see below.
Caching
If you wanted your json to cache, you can get JQuery to do something closer to my first example by setting cache: true and setting the jsonpCallback property to a string that is hardcoded into the cacheable json file. For example this static json:
loadJoe({name:'Joe'})
Could be loaded and cached in JQuery like so:
$.ajax({
url: '//outsidedomain.com/loadjoe.js',
dataType: 'jsonp',
cache: true,
jsonpCallback: 'loadJoe',
success: function(json) { ... }
});
Use the success callback instead..
function userinfo(username){
$.ajax({
url: "https://api.github.com/users/" + username,
success: getName,
dataType: "jsonp"
});
}
$(function() {
function userinfo(username){
var XHR = $.ajax({
url: "https://api.github.com/users/" + username,
dataType: "jsonp"
}).done(function(data) {
console.log(data.data.name);
});
}
users = ["torvalds", "twitter", "jquery"];
for(var i = 0; i < users.length; i++){
userinfo(users[i]);
}
}); ​
Not sure but the response I get from that call to the github API does not include gravatar_id.
This worked for me:
function getGravatar(response){
var link = response.data.avatar_url;
$('#list').append('<div><img src="' + link + '"></div>');
}

Ajax requests are not cached by jQuery

I am using below code for making ajax request. I am trying to determine request are cached or not by using chrome tool. In request tab i see all datas always pulled from server and there is no any "cache" in status text column.
How can i detect that request are cached or not. And I think result are not cached so what is wrong on my code ?
$.ajax({
url: $(this).attr('href'),
dataType:'html',
cache:true,
success: function (data) {
}
});
cache is by default set to true for HTML. If you sent to false jQuery adds a parameter in a query as in '_=201105XXXX".
If the query was not part of the URL, it means that the request page was cached.

Appending an extra request to JSON callback

My Problem
I am trying to load JSON encoded data from a remote site using jQuery, however when jQuery tries to call this URL it appends the correct function to callback=? so it's something like callback=jsonp1256856769 but it also adds _=1256856769 to the url. So the url ends up being something like http://www.example.com/link/to/file.php?format=json&lang=en&callback=jsonp1256856769&_=1256856769
Now the problem is that that file that I am using that calls it can't interpret the _=1234234 and I can't change it so I have to fix the jQuery problems
My Question
How can I get jQuery to not appened that _= to the URL that it calls
What I have done to try to figure out my problem
Removed all other javascript libraries from the page
Tried several different versions of jQuery
My Code
function getData(){
url = "http://www.example.com/link/to/file.php";
url += "?format=json&lang=en";
$.getJSON(url+"&callback=?",function(data){formatData(data);});
}
*Above is the snippet of JavaScript that I am currently using
*Note the domain I am using is not example.com
UPDATE: added code
The _= part is there, because JSONP request are cache: false by default. You can set cache: true, which will make the _= part go away, but the browser will cache the requests.
function getData() {
url = "http://www.example.com/link/to/file.php";
url += "?format=json&lang=en";
$.ajax({
'url': url,
'type': 'GET',
'dataType': 'jsonp', // this adds &callback=? by design
'cache': true,
'success': function(data) { formatData(data); }
});
}

Categories