Random API calls always give same result [duplicate] - javascript

How to prevent browsers from caching Ajax results? I have and event triggered Ajax script the displays results only when the browsers data has been cleared.
Tested in IE6 and Firefox 3.0.10

The random URL works, but it's kind of a hack. HTTP has solutions built in that should work. Try using the solution indicated here. Basically, set the headers:
"Pragma": "no-cache",
"Cache-Control": "no-store, no-cache, must-revalidate, post-check=0, pre-check=0",
"Expires": 0,
"Last-Modified": new Date(0), // January 1, 1970
"If-Modified-Since": new Date(0)

Add a random query string to the URL you are sending.
E.g. if the Ajax request is sent to "http://www.xyz.com/a"
then add a random string at the end: "http://www.xyz.com/a?q=39058459ieutm39"

I've used the jQuery {cache: false} method and it worked like a charm.
The complete code example is like this:
$.ajaxSetup({cache: false});

There are two techniques for this that I'm aware of.
Add some sort of query string to the AJAX request URL so that it's always unique. A millisecond timestamp (perhaps combined with a random value) is good for this
Set HTTP cache control headers on the AJAX response so that the browser doesn't cache it

using jQuery you can set global ajax setting: { cache: false }. See it in jquery ajax docs

Related

Navigator.sendBeacon() to pass header information

I am using navigator for communicating with the server , but problem is that we need to pass some header information as there is filter which recognise the request is from the valid source.
Can anybody help on this?
Thanks.
See the Navigator.sendBeacon MDN documentation for further information.
Create a blob to provide headers. Here is an example:
window.onunload = () => {
const body = {
id,
email,
};
const headers = {
type: 'application/json',
};
const blob = new Blob([JSON.stringify(body)], headers);
navigator.sendBeacon('url', blob);
};
navigator.sendBeacon will send a POST request with the Content-Type request header set to whatever is in headers.type. This seems to be the only header you can set in a beacon though, per W3C:
The sendBeacon method does not provide ability to customize the request method, provide custom request headers, or change other processing properties of the request and response. Applications that require non-default settings for such requests should use the [FETCH] API with keepalive flag set to true.
I was able to observe some of how this worked through this Chromium bug report.
As written in the Processing Model of sendBeacon :
Extract object's byte stream (transmittedData) and content type (contentType).
How extraction is performed is described here
What I've gathered is that the content type of the transmitted data is extracted, and it is set as the Content-Type of the HTTP request.
1) If a Blob object is sent, the Content-Type becomes the Blob's type.
2) If a FormData object is sent, the Content-Type becomes multipart/form-data
3) If a URLSearchParams object is sent, the Content-Type becomes application/x-www-form-urlencoded
4) If a normal string is sent, the Content-Type becomes text/plain
Javascript code to implement different objects can be found here
If you're using Chrome and you're trying to set the content-type header, you'll probably have some issues due to security restrictions:
Uncaught DOMException: Failed to execute 'sendBeacon' on 'Navigator': sendBeacon() with a Blob whose type is not any of the CORS-safelisted values for the Content-Type request header is disabled temporarily. See http://crbug.com/490015 for details.
See sendBeacon API not working temporarily due to security issue, any workaround?
I want to call an api when someone close the tab, so I tried to use navigator.sendBeacon() but the problem is we need to pass the Authorization token into it and sendBeacon does not provide that, so I found other solution that is more effective and very easy to implement.
The solution is a native fetch API with a keepalive flag in pagehide event.
Code
window.addEventListener('pagehide', () => {
fetch(`<URL>`, {
keepalive: true,
method: '<METHOD>',
headers: {
'content-type': 'application/json',
// any header you can pass here
},
body: JSON.stringify({ data: 'any data' }),
});
});
FAQs / TL;DR Version
Why should we need to use the keepalive flag?
The keepalive option can be used to allow the request to outlive the page. Fetch with the keepalive flag is a replacement for the Navigator.sendBeacon() API.
Learn more about it, please visit https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters
What is PageLifecycle API
Learn more about it, please visit https://developer.chrome.com/blog/page-lifecycle-api/
From the Page Lifecycle image, shouldn't unload be considered as the best choice?
unload is the best event for this case but unload is not firing in some cases on mobile and it also does not support the bfcache functionality.
I also notice that when I am using unload then I am not getting proper output in the server log. why? IDK, if you know about it then comments are welcome.
Nowadays, It's also not recommended by the developers.
Learn more about why unload is not recommended: https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes
Learn more about pagehide: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
Because the method sendBeacon(..) does not allow headers manipulation, I added them into the form as normal fields:
const formData = new FormData();
formData.append('authorization', myAuthService.getCachedToken());
navigator.sendBeacon(myURL, formData);
Then on the host side I added a simple Middleware class (.Net) which catches POST requests without headers and copies them from the body:
public class AuthMiddleware
{
...
...
public async Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader == null && context.Request.Method=="POST")
{
context.Request.Headers["Authorization"] = string.Format("Bearer {0}",
context.Request.Form["authorization"].ToString());
}
await _next.Invoke(context);
}
}
Posting as an answer as I'm not allowed to post a comment under the answer:
For Chrome, issue with navigator.sendBeacon sending Blob for with non CORS-safelisted types was fixed in Chrome version 81 so this should be safe to use now.
https://bugs.chromium.org/p/chromium/issues/detail?id=724929
For IE, an alternative in unload event is to use synchronous ajax request, as IE doesn't support sendBeacon but supports synchronous ajax call in my case.
You can't send data with JSON after Chrome 39, has been disabled due to a security concern.
You can try to send data with plain text. But don't forget the parseing text from the backend.
After searching for an answer for this question I found out that for passing header with navigator we need to pass a blob object.
For example
var headers = {type: 'application/json'};
var blob = new Blob(request, headers);
navigator.sendBeacon('url/to/send', blob);

Header cookies not being set by another subdomain?

I have two subdomais "api.domain.com" and "web.domain.com".
Now "web.domain.com" is web page written in html/javascript and "api.domain.com" is a simple restful API server written in php.
"api.domain.com" sets certain cookies in the header as follows
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Credentials: true");
setcookie("TestCookie", "Some Value", time()+3600, "/", ".domain.com", 0);
Now, when I make an ajax call (using jQuery.ajax() ) from "web.domain.com" to "api.domain.com", the response headers contain
Set-Cookie:abc=802691344656c1d0899c4a74.87956617; expires=Mon, 16-May-2016 21:00:09 GMT; path=/; domain=domain.com,
so i guess a cookie should be set in the client browser at "web.domain.com".
The next time I make another request to "api.domain.com" from "web.domain.com", shouldn't this cookie go as part of the request headers?
However, when I inspect the $_COOKIE array at "api.domain.com", i don't see this cookie! Does that mean the cookie never got set in the client ("web.domain.com") at the first place? What am I doing wrong?
Using the withCredentials header (as suggested by #charlietfl) worked for me. I had to make one more modification in the server as well.
So here's what I did.
In web.domain.com , while maqking the Ajax request, I added withCredentials: true , like this
$.ajax({
// The Url for the request
url : ajaxUrl,
// The data to send (will be converted to a query string)
data : ajaxData,
xhrFields: {
// To allow cross domain cookies
withCredentials: true
},
...
});
In api.domain.com , I set some headers like this :
header("Access-Control-Allow-Origin: *");
However, I was still unable to get any response. I got this error instead
Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
So i simply set the header to the origin domain, like so :
$http_origin = $_SERVER['HTTP_ORIGIN'];
if (substr($input, -10) == 'domain.com') { // To check if request is always from a subdomain of 'domain.com'
header("Access-Control-Allow-Origin: $http_origin");
}
That fixed the issue.

XMLHttpRequest How to know what setRequestHeader to send?

I just started using MSXML2.XMLHTTP object with VBA and I'm trying to interact with our Content Server (create, move, copy, delete files).
I found some random working code on the internet but I still do not fully understand what is going on.
Set req As New MSXML2.XMLHTTP
req.Open "MKCOL", "https://company/dav/nodes/" & URIsource & "/" & Encoded_FolderName, False
req.SetRequestHeader "Content-Type", "text/xml"
req.Send
URIsource is an objID from the server (ie : 12345678)
Encoded_Foldername is the folder name passed into this a function called URLEncode found here
From the OpenText website I tought I was supposed to put :
req.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
But it doesn't work with my code. I must use the previous one.
I also have a function to copy a file and it uses :
req.setRequestHeader "Destination", strDestURL
How am I supposed to know what Requestheader to use? Is there a list somewhere for post, put, get, etc. ? I understood that "Content-Type" is used to make sure the server understands the request but that's it.
Thank you
Request headers contain information about the sender, body of the request and required response.
http://help.dottoro.com/ljhcrlbv.php

Firefox exception 'JavaScript component does not have a method named: "available"'

I'm building a web app with Django. I have a bunch of API calls in Javascript via Ajax (jQuery v1.8.3).
Most of them work, but a particular one results in a return object with status 0 and this message as the statusText:
[Exception... "'JavaScript component does not have a method named: "available"' when calling method: [nsIInputStream::available]" nsresult: "0x80570030 (NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED)" location: "JS frame :: http://127.0.0.1:8000/media/js/jquery.js :: .send :: line 8434" data: no]
The corresponding line in jQuery is xhr.send( ( s.hasContent && s.data ) || null );
However, this occurs only in Firefox. Chrome works fine. Again, other requests do work. The only thing which sets this one apart is the DELETE http method.
The request is as follow (HTTP network data shown in Chrome – Firebug doesn't show anything in Firefox):
Request URL: http://127.0.0.1:8000/api/reservation/13/
Request Method: DELETE
Status Code: 400 BAD REQUEST (This is expected)
Request Headers
Accept: application/json, text/javascript, */*; q=0.01
Content-Length: 15
Content-Type: application/json
Origin: http://127.0.0.1:8000
Referer: http://127.0.0.1:8000/reservation/
X-Requested-With: XMLHttpRequest
Request Payload
[object Object]
Response Headers
Cache-Control: no-cache
Content-Type: text/html; charset=utf-8
Date: Tue, 02 Apr 2013 19:18:35 GMT
Server: WSGIServer/0.1 Python/2.7.2
On the server, I don't receive any request.
The JS code is (taken directly from Firebug Watch at breakpoint):
options = {
contentType: "application/json",
data: Object {},
dataType: "json",
processData: false,
type: "DELETE",
url: "/api/reservation/13/",
error: function(),
success: function()
};
$.ajax(options);
I also did try to disable all extensions in FF. I run v20.0.
The problem was a combination of Firefox with jQuery/XMLHttpRequest and sending an object via HTTP DELETE. Once JSON'ifying the object via JSON.stringify() everything worked.
Still, a strange exception for Firefox to throw.
Thanks to freddyb for that idea.
The problem was with the property called processData within the $.ajax function. When this property is supplied as "false" (don't know why) Firefox doesn't like it, and as consequence, the browser doesn't digest the JSON request/response package. Chrome and Safari works just fine.
This happens (as of 2014 with FireFox 32) with any non-GET AJAX request when the request data object is an empty object, like {}. I am using Mithril.js and it may be related to the fact that Mithril always sets a Content-Type for non-GET requests. This was absolutely repeatable once I knew the trigger.
(Note that the "non-GET" part may not be entirely accurate -- Mithril ignores the data object if it's a GET so sending an empty object with GET using the underlying AJAX object may also fail in the same way.)
Counter-intuitively, setting data to an empty string, "", does not fail in this way, so that was my work-around. I actually don't set data at all when there is none, and if it's unset by the time I send the request (in my AJAX wrapper) I default it to "".
It sounds like you have a buggy Firefox extension installed which is trying to examine the XMLHttpRequest data and failing....
I suggest you try http://support.mozilla.org/en-US/kb/troubleshoot-firefox-issues-using-safe-mode or just disabling whatever Firefox extensions are involved.

Force a reload of page in Chrome using Javascript [no cache]

I need to reload a page using JavaScript and ensure that it does not pull from the browser cache but instead reloads the page from the server.
[As elements of the page will have changed in the interim]
On IE and FF I found that the following code worked fine;
window.location.reload(true);
However it does not work on Chrome or Safari.
I tried the following, but also to no avail;
window.location.replace(location.href);
document.location.reload(true);
document.location.replace(location.href);
Is there a solution to this issue?
Findings
After looking into this I have found that this issue is HTTP Protocol handling;
Chrome sends a request with Pragma: no-cache HTTP field
Server responds with Last-Modified: DATE1 field
JS uses location.reload(true) to force a reload from server not cache
Chrome sends a request with If-Modified-Since: DATE1 field
Server responds with HTTP Status 304 Not Modified
The server application is at fault for not noticing the state change in the dynamic page content, and thus not returning a 200.
However, Chrome/WebKit is the only browser that sends a If-Modified-Since field when the JS location.reload(true) is called.
I thought I would put my findings here in-case someone else comes across the same issue.
You can use this hack:
$.ajax({
url: window.location.href,
headers: {
"Pragma": "no-cache",
"Expires": -1,
"Cache-Control": "no-cache"
}
}).done(function () {
window.location.reload(true);
});
To ensure the page isn't loaded from cache you can add some unique number to query:
window.location = location.href + '?upd=' + 123456;
You also can use date instead of 123456
This is what I do to ensure my application file is force reloaded on chrome:
var oAjax = new XMLHttpRequest;
oAjax.open( 'get', '/path/to/my/app.js' );
oAjax.setRequestHeader( 'Pragma', 'no-cache' );
oAjax.send();
oAjax.onreadystatechange = function() {
if( oAjax.readyState === 4 ) {
self.location.reload();
}
}
Try window.location = window.location
Great findings! I just encountered the same issue and this really helps a lot!
However, in addition to your finding, it seems that Chrome always sends a GET request for location.reload()...IE/FF is repeating the last request instead.

Categories