Simple API call (CORS) not working - javascript

First and foremost, I am new to working with APIs.
I am trying to make the most basic API call to a different domain (weather service) than my own, client-side. Among others, I had problems with CORS, not having a correct header present, etc., so I tried to make a workaround by directing the call through crossorigin.me (will be evident from the URL in my code).
(This is not the code, but only the problem - scroll down for code) However, my code currently results in...
console.log(status); //returns 0
console.log(statusText); //returns an empty string
This is the full code.
function createCORSRequest(method, url) {
xhr = new XMLHttpRequest();
//I work in Chrome, so I only included withCredentials, not accounting for other browsers
if ("withCredentials" in xhr) {
xhr.open(method, url, true);
}
xhr.send();
console.log(xhr.status); //returns 0
console.log(xhr.statusText); //returns empty string
}
createCORSRequest("GET", "https://crossorigin.me/https://www.metaweather.com/api/location/2459115");
If you visit the site directly, https://crossorigin.me/https://www.metaweather.com/api/location/2459115, I get this message: Origin: header is required. In console, it gives status code 403 - but I read that only the server can control/access the header, and that I (the coder) can't.

You are calling xhr.send() and then immediately trying to read the status of the response.
You have to wait until there is a response before you can do that.
Use a load event listener.
xhr.addEventListener("load", log_status);
xhr.send();
function log_status() {
console.log(this.status);
console.log(this.statusText);
}

Related

CORS for SoundCloud API doesn't work for some requests

I'm pretty new to Javascript and I'm not sure what I'm doing wrong here.
My goal is to retrieve the list of related SoundCloud tracks for a specified track (with it's id) through the SoundCloud API.
While doing my XMLHttpRequest I got this error:
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://127.0.0.1:49458' is therefore not allowed
access. The response had HTTP status code 500.
So from searching I found out, that it has to have something to do with the CORS Header SoundCloud uses to make GET requests possible.
Looking into the API documentation it says, that error code 500 menas an internal server error on SoundCloud sites. But I've tried the whole day now and I can't believe I'm just getting unlucky.
This is my requesting function:
function getRelated(soundcloudID) {
var xhr = new XMLHttpRequest();
xhr.open('GET', "https://api.soundcloud.com/tracks/" + soundcloudID +"/related?client_id=1dff55bf515582dc759594dac5ba46e9", false);
xhr.addEventListener("load", function() {
filterBubble.push(JSON.parse(xhr.response));
}, false);
xhr.send();
}
I know that this is not the best way to do such an request and I should change it into a callback function. I will do that, but I'm pretty sure this has nothing to do with it not functioning.
The request also doesn't work for the top trending songs on SoundCloud (https://api-v2.soundcloud.com/charts?kind=trending&genre=soundcloud:genres:all-music&client_id=1dff55bf515582dc759594dac5ba46e9&q=).
The funny thing now is, that I have no problem with search request via the API. XMLHttpRequests for https://api.soundcloud.com/tracks?client_id=1dff55bf515582dc759594dac5ba46e9&q= are working just fine.
What am I doing wrong here?
Or am I just stupid and it's because I'm testing with localhost?
I believe you may be calling the API with an invalid soundcloudId. Taking a look at the /tracks documentation on the SoundCloud docs there is an example track ID. When I call your function with it, everything succeeds.
So give your function a call with the following ID of 13158665:
function getRelated(soundcloudID) {
var xhr = new XMLHttpRequest();
xhr.open('GET', "https://api.soundcloud.com/tracks/" + soundcloudID + "/related?client_id=1dff55bf515582dc759594dac5ba46e9", false);
xhr.addEventListener("load", function() {
console.log(xhr.response)
}, false);
xhr.send();
}
// works
getRelated('13158665')
// error you described
getRelated('random id')

How to get data from an external provider in CodePen?

I'm doing the JavaScript challenges at FreeCodeCamp. One of them is to create a web page that retrieves and displays weather information.
First, I tried to use several providers (e. g. OpenWeatherMap, WeatherUnderground), which use the HTTP protocol to return weather data. It didn't work because of the mixed content error.
Next, I switched to a provider, which delivers the data via HTTPS. I got rid of the mixed content problem, but got another one:
XMLHttpRequest cannot load https://api.weatherbit.io/v1.0/current?lat=55.7767723&lon=37.6090795&units=S&key=XXXXXXXX. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://s.codepen.io' is therefore not allowed access. The response had HTTP status code 403.
I tried to implement CORS according to this tutorial:
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
}
[...]
var url = "https://api.weatherbit.io/v1.0/current?lat=" + position.coords.latitude + "&lon=" + position.coords.longitude + "&units=S&key=XXXXXXXX";
var xhr = createCORSRequest('GET', url);
if (xhr) {
xhr.onload = function() {
var responseText = xhr.responseText;
console.log("Response: " + responseText);
};
xhr.onerror = function() {
console.log('There was an error!');
};
xhr.send();
}
When I call xhr.send() I still get the error.
How can I fix it?
Note: I'm looking for a solution that will run in CodePen.
Update 1 (23.03.2017 11:35 MSK): I tried to implement sideshowbarker's answer and modified the code like this:
function getCurrent(json){
console.log("getCurrent called");
console.log(json.data.temp);
console.log(json.data.precip);
}
function updateWeather() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var url = "https://api.weatherbit.io/v1.0/current?callback=getCurrent&lat=" + position.coords.latitude + "&lon=" + position.coords.longitude + "&units=S&key=XXXXXXXXX";
console.log("url: " + url);
$.get(url, function(val){});
});
} else {
console.log("Cannot obtain location data");
};
}
updateWeather();
The result:
Update 2 (23.03.2017 13:29 MSK): This one works.
function getCurrent(json) {
console.log("getCurrent called");
console.log(JSON.stringify(json));
// TODO: Update the UI here
}
function updateWeather() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var url = "https://api.darksky.net/forecast/XXXXXXXXX/37.8267,-122.4233?callback=getCurrent";
var script = document.createElement('script');
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
});
}
}
updateWeather();
The Weatherbit.io API doesn’t support cross-origin requests made from XHR.
Instead the API requires you make the requests using a script element with a JSONP callback:
<script>
function getCurrent(json){
console.log(json.data.temp)
console.log(json.data.precip)
}
</script>
<script
src="https://api.weatherbit.io/v1.0/current?callback=getCurrent&lat=NNN&lon=NNN&units=S&key=XX"></script>
Of course you likely want to have your code inject that script element with the URL and params.
That’s method of injecting the script element with a JSONP callback is the only direct method they support for using their API from a web app.
There’s no way your code will work if it instead makes the request to their API using XHR; they don’t send the necessary Access-Control-Allow-Origin response header, and so because that’s missing, your browser won’t let your client-side JavaScript access the response cross-origin.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS explains why.
The only way you could use XHR to work with their API is if you set up a CORS proxy using code from https://github.com/Rob--W/cors-anywhere/ or something similar—or if you send your request through an public CORS proxy like https://github.com/Rob--W/cors-anywhere/ (which you don’t want to do because that’d give the owner of that service access to your Weatherbit.io API key).
The way the proxy works is that instead of using weatherbit.io URL, you use a proxy URL like https://cors-anywhere.herokuapp.com/https://api.weatherbit.io/v1.0/current…, and the proxy sends it on to weatherbit.io, gets the response back, then adds the Access-Control-Allow-Origin response header to the response it hands back to your code and that the browser sees.
I was in the process of completing the Weather App in FCC and came across the same issue. I was able to get it to work with the following line:
$.getJSON("https://api.weatherbit.io/v1.0/current?lat=##&lon=##&key=##", function(data){};
For whatever reason, it wouldn't work with just "http://", I had to change it to "https://" in order for it to work.
Not sure if that helps anyone in the future.

Get image data from another domain with AJAX request

I am trying to get binary data of an image from another domain with an AJAX request. I tried various methods, but there was no working solution. I found some code on the internet that looked good, but even with this calls I get errors.
What do I wrong? Is there a standardized way to do this?
Here is what I tried until now:
var request = this.createCORSRequest('GET', 'http://url/to/image.png');
request.onload = function () {
var text = request.response;
};
request.onerror = function (error) {
alert('Woops, there was an error making the request.');
};
request.send();
private createCORSRequest(method, url) {
var xhr: XMLHttpRequest = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
var xdhr = new XDomainRequest();
xdhr.open(method, url);
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
}
I even found this solution without ajax here on stackoverflow, but it does not work for me:
Asynchronously load images with jQuery
Here a screen of the properties the error event contains:
My goal is to get the binary of an image from a url which I get from an atom feed . I need the binaries to copy the picture to MS SharePoint.
You cannot get data from another domain unless :
the remote server allows it using CORS
you run your browser in an unsafe mode.
Reason : otherwise site A would be able to (maliciously) read the user data from site B
You must add headers to the method to allow cross domain request.
For example, if you are trying to get data from www.example.com/main.php , then you must add headers to allow those method to be called from different domain.

Changing path type causes ajax to fail?

I found this bizarre, but I recently change all my paths from relative to absolute.
I see that ajax appears to be working fine in the console as I can see the files retrieved successfully, with a status of 200.
Here is a pic: (its small but hopefully you can make out the status 200)
However, my callback functions stopped running, here is the code:
if (config_ajax.type === 'get') {
xhr = new win.XMLHttpRequest();
xhr.open('GET', config_ajax.url, true);
xhr.onload = function () {
if (this.status === 200) {
$A.log('succeeded with status 200'); // never gets here
config_ajax.callback(xhr.responseText);
}
};
xhr.send(null);
}
you have an incorrectly formatted request to the server as shown in firebug
http://www.arcmarks.com/http://www.arcmarks.com/arcmarks/source/class.CMachine.php
note the http://www shows twice
If the page is at arcmarks.com, it cannot make AJAX requests to www.arcmarks.com - browsers enforce something called the Same Origin Policy which prevents you from sending AJAX requests to any domain other than the exact one the original page was served from.
Also, the comment about the request being sent to www.www.arcmarks.com is right - as the code adds a "www" to the current URL, if your URL has a www in it already it will be repeated. But I'm assuming this was intentional.

using javascript to detect whether the url exists before display in iframe

I'm using javascript to pass a dynamic url to iframe src. but sometimes the url does not exist, how could i detect the non-exist url beforehand, so that i can hide the iframe that with 404 error.
Due to my low reputation I couldn't comment on Derek 朕會功夫's answer.
I've tried that code as it is and it didn't work well. There are three issues on Derek 朕會功夫's code.
The first is that the time to async send the request and change its property 'status' is slower than to execute the next expression - if(request.status === "404"). So the request.status will eventually, due to internet band, remain on status 0 (zero), and it won't achieve the code right below if. To fix that is easy: change 'true' to 'false' on method open of the ajax request. This will cause a brief (or not so) block on your code (due to synchronous call), but will change the status of the request before reaching the test on if.
The second is that the status is an integer. Using '===' javascript comparison operator you're trying to compare if the left side object is identical to one on the right side. To make this work there are two ways:
Remove the quotes that surrounds 404, making it an integer;
Use the javascript's operator '==' so you will be testing if the two objects are similar.
The third is that the object XMLHttpRequest only works on newer browsers (Firefox, Chrome and IE7+). If you want that snippet to work on all browsers you have to do in the way W3Schools suggests: w3schools ajax
The code that really worked for me was:
var request;
if(window.XMLHttpRequest)
request = new XMLHttpRequest();
else
request = new ActiveXObject("Microsoft.XMLHTTP");
request.open('GET', 'http://www.mozilla.org', false);
request.send(); // there will be a 'pause' here until the response to come.
// the object request will be actually modified
if (request.status === 404) {
alert("The page you are trying to reach is not available.");
}
Use a XHR and see if it responds you a 404 or not.
var request = new XMLHttpRequest();
request.open('GET', 'http://www.mozilla.org', true);
request.onreadystatechange = function(){
if (request.readyState === 4){
if (request.status === 404) {
alert("Oh no, it does not exist!");
}
}
};
request.send();
But notice that it will only work on the same origin. For another host, you will have to use a server-side language to do that, which you will have to figure it out by yourself.
I found this worked in my scenario.
The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callback methods introduced in jQuery 1.5 are deprecated as of jQuery 1.8. To prepare your code for their eventual removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.
$.get("urlToCheck.com").done(function () {
alert("success");
}).fail(function () {
alert("failed.");
});
I created this method, it is ideal because it aborts the connection without downloading it in its entirety, ideal for checking if videos or large images exist, decreasing the response time and the need to download the entire file
// if-url-exist.js v1
function ifUrlExist(url, callback) {
let request = new XMLHttpRequest;
request.open('GET', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('Accept', '*/*');
request.onprogress = function(event) {
let status = event.target.status;
let statusFirstNumber = (status).toString()[0];
switch (statusFirstNumber) {
case '2':
request.abort();
return callback(true);
default:
request.abort();
return callback(false);
};
};
request.send('');
};
Example of use:
ifUrlExist(url, function(exists) {
console.log(exists);
});
You could test the url via AJAX and read the status code - that is if the URL is in the same domain.
If it's a remote domain, you could have a server script on your own domain check out a remote URL.
Using async/await, this worked for me for opening a new tab; I needed to detect a 404 for the same reason as the OP:
openHelp : async function(iPossiblyBogusURL) {
const defaultURL = `http://guaranteedToWork.xyz`;
const response = await fetch(iPossiblyBogusURL);
if (response.status == 200) {
window.open(iPossiblyBogusURL, `_blank`);
} else if (response.status === 404) {
window.open(defaultURL, `_blank`);
}
},
You can try and do a simple GET on the page, if you get a 200 back it means the page exists. Try this (using jQuery), the function is the success callback function on a successful page load. Note this will only work on sites within your domain to prevent XSS. Other domains will have to be handled server side
$.get(
yourURL,
function(data, textStatus, jqXHR) {
//load the iframe here...
}
);
There is no need to make a separate HTTP request to check beforehand.
You could switch the logic around: only display the iframe if it has been loaded successfully. For this purpose, you can attach an onload event listener to the iframe.
See this related question for details: Capture iframe load complete event

Categories