Why doesn't d3.json send cookies with the request? - javascript

I'm new to doing ajax requests with the built-in methods in d3.js (v5). Here's my code:
d3.json(uri).then(data =>console.log(data));
I tried this in an app that uses cookie authentication, and kept getting 401 status codes. Using the chrome dev tools revealed that it's sending the request without any cookies at all.
That's weird because ajax requests in native javascript send cookies along with every request by default. Here's an example of an ajax request in native javascript:
function nativeAjax(uri, callback) {
let request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState === 4) {
callback(request);
}
}
request.open('get', uri, true);
request.send(null);
}
nativeAjax(uri, request => console.log(request.status));
Plugging this into my app, chrome dev tools shows it does send the authentication cookie along with the request and indeed the request.status comes back as 200 showing that it is indeed authenticated.
My questions are:
how can I configure d3.json to send the required cookie?
how do I catch the response by status? I'm going to want to do something different for 401 response than 403 response, for example.
Where can I read a more complete documentation or examples for how to do use d3.json? I've always done native ajax because I don't use jquery, but if it's part of the library I'm using anyway, I'd like to learn how to use it. But the documentation says almost nothing about it, and the page it links to isn't helpful either. And most tutorials were for previous versions of d3 and don't work anymore.

Version 5 switched to use the Fetch API, which doesn't send any cookies by default. You can overcome this by adding options for d3.json to pass through to fetch:
d3.json(uri, {credentials: "same-origin"}).then(...);
Or, for cross-origin requests:
d3.json(uri, {credentials: "include"}).then(...);
If a 4XX status (or 5XX) is returned D3 will cause the promise to reject, which you can handle by providing a second callback function to then. I welcome corrections, but I believe there is no way within this function to get the actual status code.
The only mention of the change to Fetch and promises I found in the documentation (at the time of writing) was in the changelog: D3 v5 Changes.

Related

How do I set a default Header for all XMLHTTPRequests

Problem description
We are running a Kibana 4.3 service. I do not want to modify the source code.
The objective is add an encrypted token, call it A-Token to every Ajax request that the browser makes to Kibana.
Background
The Kibana service is proxied by nginx.
When a user makes an Ajax request to the Kibana service, the request is intercepted by an nginx http_auth_request proxy and passed to an "auth" service that validates the token. If its missing or invalid, then "auth" returns 201 to http_auth_request and the request to the Kibana service is executed, else it returns a 404 and the request is denied since it was made without a valid token.
(this scheme is based on the encrypted token pattern often used as a countermeasure for cross-site scripting in session-less situations like the one at hand).
I read the W3 XMLHttpRequest documentation and it seems that setRequestHeader needs to run after open and before send - which implies that this scheme is either impossible in a general case or very JS platform dependent.
A test using the Jquery .ajaxSetup like this example, confirms that headers cannot be set independently:
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader(A-Token", 1314159);
}
});
Looking for possible solutions which will not require forking Kibana.
Danny
I was searching for solution for this problem as well but couldn't find anything and then I came up with next solution:
XMLHttpRequest.prototype.origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function () {
this.origOpen.apply(this, arguments);
this.setRequestHeader('X-TOKEN', 'the token');
};

XMLHTTPRequest Not returning

I created a java script to connect to my web service however I never return from the send method of my request:
var request = new XMLHttpRequest();
request.overrideMimeType('text/plain');
request.open("GET", url, false);
request.send(null);
alert("Complete");
I never see the alert. However when I step through my service it returns successfully so it has to be with this script.
Note: I can run the url from a browser, chorme or firefox tested, and I am able to get the response I want. I just cannot get it from js.
My web service was not set up to receive HTTP GET request (REST based service). I was only set up to handle SOAP based requests.
Perhaps it is because of the false in request.open(), which means that the request is synchronous, so the rest of the script won't continue executing until the response has arrived from the server. Does the server return the response package? If it doesn't, you should try changing the third argument of the function to true, it should continue with the script execution then.
Check this link out: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#send()

POST CORS request to HTTPS domain running sailsjs

I got caught in problems with CORS and HTTPS and POST request and just cannot get out of it.
My setup:
I have a web app that runs on HTTPS protocol
I need to make POST request to API on different domain (sailsjs with CORS enabled)
The API is setup to accept both HTTP and HTTPS requests (nginx)
Problems:
On desktop everything works fine if I use HTTPS protocol. If I make call to HTTP, I get error: "Blocked loading mixed active content"
On Mobile I spent days and days trying to make work HTTPS calls, I tried hundereds of ways, to no success. HTTP calls work fine, but again cannot use HTTPS to HTTP.
So basically, on desktop everything works splendid while everything is on https. On mobile it doesn't work. When switching to http target everything brakes because of mixed origin.
More Details
The webapp is using Angular which is making CORS POST HTTPS requests without any problems on all platforms.
The small part that I am trying to make work is using vanilla xhr request:
submit: function(data){
var request = this.createCORSRequest("POST",'https://heregoesthedomain.com');
if (request){
request.onload = function() {
// Success code goes here.
console.log('success');
};
request.onerror = function() {
// Error code goes here.
console.log('error');
};
request.send(JSON.stringify(data));
}
},
createCORSRequest: function(method, url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Most browsers.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// IE8 & IE9
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
xhr = null;
}
return xhr;
}
Angular seems to be making post requests from HTTPS to HTTPS from domain to domain without a problem across all platforms, while I just cannot seem to make it work in clean JS. I cannot use Angular script, because I need these calls to happen before Angular is initialized.
Update 1
Just to make clear, I didn't post this question after 5 minutes of trying. I have done all kinds of debugging, I have used remote consoles for mobile browser debugging etc etc.
If you ask for the error messages, there simply isn't any. Request returns status:0 and all other XMLHttp values are simply empty. I have read in one of the specs that request get sometime blocked when its been made from origin that is no longer active/doesnt exist, but that is not my case. Simply staying on the same static page.
Since you API works with both types of schemes you could make the requesting URL match the scheme of the page (using a schema less url)
this.createCORSRequest("POST",'//heregoesthedomain.com')
This should solve the
Blocked loading mixed active content
problem.
Also on IE8/IE9 you cannot do normal way any CORS request. You can sort it by using XDomain
Never then less still I will say that for web browser you will get problem with mix content. Only solution is creating special route so you will have http and other calls on https.
If you have Netflix look that they sort it out by playing movie on http because there movie chunks are get by http and session is done by https

Not able to access Web API method using XMLHttpRequest but using Restclient Plugin and etc

I have run in to a problem. Please help with your expertise.
I am developing web solution for a company. They have provided me Web API Method (REST). This API is in their domain. I am too access this from my domain. Even client has also already whitelisted my domain.
I am trying to call this method using below. But no luck. I am getting this below error.
Error NS_ERROR_DOM_BAD_URI: Access to restricted URI denied
function GetCustomerInfo()
{
var Url = "http://webapi.company.com/vx.x/customer/get?format=xml&mobile=9876543210" ;
myXmlHttp = new XMLHttpRequest();
myXmlHttp.withCredentials = true;
myXmlHttp.onreadystatechange = ProcessRequest;
myXmlHttp.open( "GET", Url, true,"UID","PWD");
myXmlHttp.send();
}
function ProcessRequest()
{
if(this.readyState == this.DONE)
{
if(this.status == 200 && this.responseXML != null )
{
alert ("Received Resoponse from Server");
}
else
{
alert ("Some Problem");
}
}
}
I am able to access this method from RESTClient in firefox plugin.
Even I am able to access this method copying credentials in URL as below in browser address bar. I get anticipated response XML
http://UID:PWD#webapi.company.com/vx.x/customer/get?format=xml&mobile=9876543210
Please enlighten me where I am wrong. Perhaps JSONP can come to my help. But why i am not able to access this API using XMLHttpRequest.
Regards
Rajul
The same origin policy of the browser does not allow you to send XMLHttpRequests to a different domain. The reason you can access it through a firefox plugin or the address bar is that the same origin policy is not applied there.
You are right, JSONP could solve your problem, although you may run into trouble because you do not control the serverside.
In response to your comment: In order to use JSONP effectively, the server will need to return not only the data you need in JSON format, but also javascript code to invoke a callback when the request is done. If you do not control the data that is returned, you can not add the necessary code for this. The wikipedia article gives a good example for the general case.
I have never used CORS, thus can not give you much information on it. It seems like a better solution, but I imagine it is not incredibly compatible across browsers. Also, as I understand it, you will need control of the server as well, as it seems to require additional HTTP headers.

How to prevent ajax requests to follow redirects using jQuery

I use the jQuery ajax functions to access a web service, but the server, instead of returning a response with a status code describing a problem, the request is redirected to a page with a 200 header, describing the problem. I can't make any changes to this, so I need to solve it on the client somehow.
Example:
A request goes to some URL which is not found, so I receive a 302 Redirect to another location. A new request is sent, and I receive a 200 OK, thus preventing the error callback to fire.
Is there some way I can prevent the ajax request to follow redirects and instead invoke a callback, preferably the error method. Alternatively, is it possible to detect if a redirect has happened in the client?
I find your question interesting, but the problem in whole seems me more a misunderstanding. At least I'll try to explain my understanding of the problem.
The silent (transparent) redirection is the part of XMLHttpRequest specification (see here especially the words "... transparently follow the redirect ..."). The standard mention only that the user agent (the web browser) can prevent or notify of certain kinds of automatic redirections, but it's not a part of XMLHttpRequest. It's the part of HTTP client configuration (OS configuration) or the web browser configuration. So jQuery.ajax can't have any option where you can prevent redirection.
You can see that HTTP redirection is the part of HTTP protocol and not a part of XMLHttpRequest. So it's on the another level of abstraction or the network stack. For example the data from the XMLHttpRequest can be retrieved from the HTTP proxy or from the local browser cache, and it's the part of HTTP protocol. Mostly the server which provide the data and not the client can influence on caching.
You can compare the requirement from your question with the requirement to prevent changing of IP address of the web server or the changing of the IP route during the communication. All the things can be interesting in some scenarios, but there are parts of another level of the communication stack and can't be managed by jQuery.ajax or XMLHttpRequest.
The XMLHttpRequest standard say that the client configuration can have options which prevent redirection. In case of "Microsoft world", which I better know, you can look at WinHttpSetOption function which can be used to set WINHTTP_OPTION_DISABLE_FEATURE option with the WINHTTP_DISABLE_REDIRECTS value. Another way are the usage of WINHTTP_OPTION_REDIRECT_POLICY option with the WINHTTP_OPTION_REDIRECT_POLICY_NEVER value. One more feature which one can use in Windows is the WinHttpSetStatusCallback function which can set callback function received some notifications like WINHTTP_CALLBACK_FLAG_REDIRECT.
So it's do possible to implement your requirements in general, but the solution will be probably not independent from the operation system or the web browser and be not on the level of jQuery.ajax or XMLHttpRequest.
I don't believe it is possible. The underlying library (XHR) makes the new request transparently. That being said, what I have done in these situations (usually a session-timeout type of deal that takes me to a login page) is send back a custom response header. I also have setup a global ajax handler that checks for the presence of that header, and responds appropriately when present (for example, redirecting the whole page to the login screen).
In case you're interested, here's the jQuery code I have to watch for that custom header:
/* redirects main window when AJAX request indicates that the session has expired on the backend. */
function checkSession(event, xhr, ajaxOptions)
{
if (xhr.readyState == 4)
{
if(xhr.getResponseHeader("Login-Screen") != null && xhr.getResponseHeader("Login-Screen").length)
{
window.location.href='sessionExpired.html'; //whatever
}
}
}
$(document).ajaxComplete(checkSession)
I found a feature to check if your call has been redirected. It's xhr.state(): if it's "rejected" then a redirection happened.
Example with success callback:
request.success(function(data, textStatus, xhr)
{
if(xhr.state() == "resolved")
{
//no redirection
}
if(xhr.state() == "rejected")
{
//redirection
}
});
Example with error callback:
request.error(function(xhr, textStatus)
{
if (xhr.state() == "rejected")
{
//redirection
location.href = "loginpage";
} else
{
//some other error happened
alert("error");
}
});
I can't possibly add to the insightful wisdom of the previous coders who've responded, but I will add a specific case that others may find useful to know about.
I came across this 302 silent redirect in the context of SharePoint. I have some simple Javascript client code that pings a SharePoint sub-site, and if it receives a 200 HTTP response, it relocates to that site, via window.location. If it receives anything else, it gives the user a notice that the site doesn't exist.
However, in the case where the site exists but the user does not have permission, SharePoint silently redirects to an AccessDenied.aspx page. SharePoint has already done the HTTP 401 authentication handshake at the server/farm level - the user has access to SharePoint. But the access to the sub-site is handled I suppose using database flags of some sort. The silent redirect bypasses my "else" clause, so I can't throw up my own error. In my case, this is not a show-stopper - it is consistent predictable behavior. But it was a little surprising, and I learned something about HTTP requests in the process!
I was interested in the same thing and could not find the state() method mentioned by Takman and did a little digging for myself. For the sake of people turning up here in search of an answer, here are my findings:
As stated multiple times, you cannot prevent redirects, but you can detect them. According to MDN you can use the responseURL of the XMLHttpRequestObject, which will contain the final URL the response came from, after all redirects. Only caveat is that it is not supported by Internet Explorer (Edge has it). Since the xhr/jqXHR passed into the success/done function of jquery is an extension of the actual XMLHttpRequest, it should be available there, too.
While it is not possible to disable location redirect following in XmlHttpRequests, it is when using fetch():
fetch('url', {redirect: manual});
I suppose you receive a 200 response because the second time there is no redirection, because the 404 page does not expire, it is saved in the cache. That is to say that the second time the browser gives you the page in the cache.
There is a property "cache" in the ajax jquery.
http://api.jquery.com/jQuery.ajax/
You should write it to "false"
I'm not sure if this will apply in your case, but you can write code to respond to specific status codes in AJAX function -
$.ajax({
url: '/admin/secret/data',
type: 'POST',
contentType: 'application/json; charset=utf-8',
statusCode: {
200: function (data) {
alert('302: Occurred');
// Bind the JSON data to the UI
},
401: function (data) {
alert('401: Occurred');
// Handle the 401 error here.
}
}
});
In the request headers in the case of ajax request you will have the following
X-Requested-With XMLHttpRequest
By this criteria on the server side you can filter requests.

Categories

Resources