I am trying to query the App Store for information on a given app, however I keep getting the following error.
XMLHttpRequest cannot load https://itunes.apple.com/lookup?id=<some-app-id>.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://www.<some-website>.co.uk' is therefore not allowed access. The response had HTTP status code 501.
The code I'm using to execute the request is as follows.
Does anyone know where I may be going wrong?
var config = {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With',
}
};
$http.get("https://itunes.apple.com/lookup?id=<some-app-id>", config).success(
function(data) {
// I got some data back!
}
);
You can use $http.jsonp,
$http.jsonp("https://itunes.apple.com/lookup", {
params: {
'callback': 'functionName',
'id': 'some-app-id'
}
});
Where functionName is the name of your globally defined function in string form. You can redefine it in your module so that it has access to $scope.
Documentation
Edit: here's a plunker showing my successful approach roughly getting it into an AngularJS app:
http://plnkr.co/edit/QhRjw8dzK6Ob4mCu6T6Z?p=preview
Adding those headers to your server won't change what is happening. The cross origin headers need to be added by the iTunes API.
That is not going to happen, so what you need to do instead is to use JSONP style callbacks in your webpage. There is an example on the iTunes search API page.
http://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html
Note: When creating search fields and scripts for your website, you
should use dynamic script tags for your xmlhttp script call requests.
For example:
<script src="https://.../search?parameterkeyvalue&callback="{name of JavaScript function in webpage}"/>
Note the 'callback' parameter there. That is a function defined globally in your javascript on the page that will get called with the response from the request to the url in 'src'. That function puts the data into your page, or application. You'll have to figure out how.
It's a shame that the language used in this documentation is not clearer, because you must do some kind of JSONP style workaround since they don't have CORS enabled on their API.
If you need to dynamically add a script tag (fetching data once is not enough) you can try this tutorial:
Dynamically add script tag with src that may include document.write
The API in general is probably intended for use by backends (not affected by cross origin issues), not for client side fetching.
Using angular 2:
constructor(private _jsonp: Jsonp) {}
public getData(term: string): Observable<any> {
return this._jsonp.request(itunesSearchUrl)
.map(res => {
console.log(res);
});
}
Related
Question
I need to parse an RSS feed and display the parsed details in an HTML page.
Solution I Found
How to parse an RSS feed using JavaScript? is a very similar question and I followed it.
Using above question, I build the following code.
<script>
$(document).ready(function() {
//feed to parse
var feed = "https://feeds.feedburner.com/raymondcamdensblog?format=xml";
$.ajax(feed, {
accepts:{
xml:"application/rss+xml"
},
dataType:"xml",
success:function(data) {
//Credit: http://stackoverflow.com/questions/10943544/how-to-parse-an-rss-feed-using-javascript
$(data).find("item").each(function () { // or "item" or whatever suits your feed
var el = $(this);
document.write("------------------------");
document.write("title : " + el.find("title").text());
document.write("link : " + el.find("link").text());
document.write("description: " + el.find("description").text());
});
}
});
});
</script>
The Error
Failed to load
https://feeds.feedburner.com/raymondcamdensblog?format=xml: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost' is therefore not allowed access.
What I need
How can I change my code to read RSS feeds using JavaScript without getting above error?
You could use something like https://rss2json.com.
It parses the feed to json for javascript:
var feedURL = "https://feeds.feedburner.com/raymondcamdensblog?format=xml";
$.ajax({
type: 'GET',
url: "https://api.rss2json.com/v1/api.json?rss_url=" + feedURL,
dataType: 'jsonp',
success: function(result) {
console.log(result);
}
});
You're getting that error because of the same-origin policy. See below and/or read the full article at MDN:
For security reasons, browsers restrict cross-origin HTTP requests
initiated from within scripts. For example, XMLHttpRequest and the
Fetch API follow the same-origin policy. This means that a web
application using those APIs can only request HTTP resources from the
same origin the application was loaded from, unless the response
from the other origin includes the right CORS headers.
So your script is making a cross-origin HTTP request (which uses XMLHttpRequest through jQuery.ajax()) to https://feeds.feedburner.com/raymondcamdensblog?format=xml, but the CORS header of Access-Control-Allow-Origin is not being set by FeedBurner, therefore you get the "Failed to load ..." error. (But even if the header was set, if it didn't include your origin (localhost or some-domain.com), you'd still get the same error.)
So how can you change your code to read the RSS feeds using JavaScript without getting that error?
Use a third-party web service, just like what #Saeed suggested.
Create a server-side script (e.g. using PHP) that fetches the feed content and make AJAX requests to that script instead of directly requesting it from FeedBurner, or the actual source URL. See below for a simple example.
If I really had to, I'd probably ask FeedBurner to set the appropriate CORS headers...
Sample of a very simple PHP script for fetching the feed content:
<?php
// Set the feed URL.
$feed_url = 'https://feeds.feedburner.com/raymondcamdensblog?format=xml';
// Fetch the content.
// See http://php.net/manual/en/function.file-get-contents.php for more
// information about the file_get_contents() function.
$content = file_get_contents( $feed_url );
// Set the Content-Type header.
header( 'Content-Type: application/rss+xml' );
// Display the content and exit.
echo $content;
exit;
?>
So for example, you could save that to fetch-feed.php, and then in your JavaScript/jQuery script code, change the value of the feed variable like so:
var feed = "http://localhost/path/to/fetch-feed.php";
That way (i.e. using your own server-side script), you could at least be sure that the browser would always grant your XMLHttpRequest (or AJAX) request. (i.e. you wouldn't get the "No 'Access-Control-Allow-Origin' header" error)
You can also use jquery-rss or Vanilla RSS, which comes with nice templating and is super easy to use:
// Example for jquery.rss
$("#your-div").rss("https://feeds.feedburner.com/raymondcamdensblog?format=xml", {
limit: 3,
layoutTemplate: '<ul class="inline">{entries}</ul>',
entryTemplate: '<li>[{author}#{date}] {title}<br/>{shortBodyPlain}</li>'
})
// Example for Vanilla RSS
const RSS = require('vanilla-rss');
const rss = new RSS(
document.querySelector("#your-div"),
"https://feeds.feedburner.com/raymondcamdensblog?format=xml",
{
// options go here
}
);
rss.render().then(() => {
console.log('Everything is loaded and rendered');
});
See http://jsfiddle.net/sdepold/ozq2dn9e/1/ for a working example.
It's a CORS related error. You are getting that error because the URL from where you are requesting data does not have CORS enabled. CORS stands for 'Cross-Origin Resource Sharing'. If CORS is enabled on a server, your browser will let you make requests to that server. Otherwise, it will not.
https://feeds.feedburner.com/raymondcamdensblog?format=xml does not have CORS enabled, that's why your browser will not allow you to make ajax requests to that server. You can get around it by making the requests on your server and provide the data to the browser from your own server or a server that has CORS enabled.
I am trying to get information from a fantasy data API using AngularJS. I am using $resource to perform my get request in my controller, but I haven't been able to figure out how to correctly include the API key. Do I need to include it as a header? Thanks.
nflApp.controller('mainController', ['$scope','$resource','$routeParams', function($scope, $resource, $routeParams) {
$scope.fantasyAPI = $resource("https://api.fantasydata.net/nfl/v2/JSON/DailyFantasyPlayers/2015-DEC-28", { callback: "JSON_CALLBACK" }, { get: { method: "JSONP"}});
console.log($scope.fantasyAPI);
}]);
Below is the http request info from the site.
You should set a header with the API key, AngularJS will send them with every request in the following case:
$http.defaults.headers.common["Ocp-Apim-Subscription-Key"] = key;
When adding '.common' you are telling angular to send this in every request so you do not need to add it to every resource that hits the API.
A easy way to do that is by creating your own interceptors from $httpProvider at "config" fase.
To do that, just write something like:
mymodule.config(['$httpProvider', function($httpProvider){
$httpProvider.interceptors.push(function ($q) {
return {
'request': function (config) {
config.headers['Ocp-Apim-Subscription-Key'] = SomeUserClass.AuthToken();
return config;
},
'response': function (response) {
return response;
}
};
});
});
You need to modify request header in JSONP. Unfortunately it is not possible. As the browser is responsible for header creation and you just can't manipulate that when using JSONP method.
how to change the headers for angularjs $http.jsonp
Set Headers with jQuery.ajax and JSONP?
From that link - https://johnnywey.wordpress.com/2012/05/20/jsonp-how-does-it-work/
Why NOT To Use JSONP?
Deciding against using JSONP is directly related to how it works. First of all, the only HTTP method you can use is GET since that is the only method script tags support. This immediately eliminates the use of JSONP as an option to interact with nice RESTful APIs that use other HTTP verbs to do fun stuff like CRUD. And while we’re on the subject of GET, note that using anything other than URL parameters to communicate with the server API (e.g. sending some JSON over) is also not possible. (You could encode JSON as a URL parameter, but shame on you for even thinking that.)
If they only work with header manipulation you will need to do that call from your server side.
I have an error reporting beacon I created using Google Apps script and it is published to run as myself and to be accessible to "anyone, even anonymous," which should mean that X-domain requests to GAS are allowed.
However, my browsers are now indicating there is no Access-Control-Allow-Origin header on the response after the code posts to the beacon.
Am I missing something here? This used to work as recently as two months ago. So long as the GAS was published for public access, then it was setting the Access-Control-Allow-Origin header.
In Google Apps Script:
Code.gs
function doPost(data){
if(data){
//Do Something
}
return ContentService.createTextOutput("{status:'okay'}", ContentService.MimeType.JSON);
}
Client Side:
script.js
$.post(beacon_url, data, null, "json");
When making calls to a contentservice script I always have sent a callback for JSONP. Since GAS does not support CORS this is the only reliable way to ensure your app doesn't break when x-domain issues arrive.
Making a call in jQuery just add "&callback=?". It will figure everything else out.
var url = "https://script.google.com/macros/s/{YourProjectId}/exec?offset="+offset+"&baseDate="+baseDate+"&callback=?";
$.getJSON( url,function( returnValue ){...});
On the server side
function doGet(e){
var callback = e.parameter.callback;
//do stuff ...
return ContentService.createTextOutput(callback+'('+ JSON.stringify(returnValue)+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
}
I've lost a couple of hours with the same issue. The solution was trivial.
When you deploy the script as webapp, you get two URLs: the /dev one and the /exec one. You should use /exec one to make cross domain POST requests. The /dev one is always private: it requires to be authorized and doesn't set *Allow-Origin header.
PS.: The /exec one seems to be frozen — it doesn't reflect any changes of code until you manually deploy it with a new version string (dropdown list in deploy dialog). To debug the most recent version of the script with the /dev URL just install an alternative browser and disable it's web-security features (--disable-web-security in GoogleChrome).
Just to make it simpler for those who are only interested in a POST request like me:
function doPost(e){
//do stuff ...
var MyResponse = "It Works!";
return ContentService.createTextOutput(MyResponse).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
I stumbled upon the same issue:
calling /exec-urls from the browser went fine when running a webpage on localhost
throws crossorigin-error when called from a https-domain
I was trying to avoid refactoring my POST JSON-clientcode into JSONP (I was skeptical, since things always worked before).
Possible Fix #1
Luckily, after I did one non-CORS request (fetch() in the browser from a https-domain, using mode: no-cors), the usual CORS-requests worked fine again.
last thoughts
A last explanation might be: every new appscript-deployment needs a bit of time/usage before its configuration actually settled down at server-level.
Following solution works for me
In Google Apps Script
function doPost(e) {
return ContentService.createTextOutput(JSON.stringify({status: "success", "data": "my-data"})).setMimeType(ContentService.MimeType.JSON);
}
In JavaScript
fetch(URL, {
redirect: "follow",
method: "POST",
body: JSON.stringify(DATA),
headers: {
"Content-Type": "text/plain;charset=utf-8",
},
})
Notice the attribute redirect: "follow" which is very very important. Without that, it doesn't work for me.
I faced a similar issue of CORS policy error when I tried to integrate the app script application with another Vue application.
Please be careful with the following configurations:
Project version should be NEW for every deployment.
Execute the app as me in case you want to give access to all.
Who has access to the app to anyone, anonymous.
Hope this works for you.
in your calling application, just set the content-type to text/plain, and you will be able to parse the returned JSON from GAS as a valid json object.
Here is my JSON object in my google script doPost function
var result = {
status: 200,
error: 'None',
rowID: rowID
};
ws.appendRow(rowContents);
return ContentService.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON);
and here I am calling my app script API from node js
const requestOptions = {
method: 'POST',
headers: {'Content-Type': 'text/plain'},
body: JSON.stringify({param1: value, param2:value})
};
const response = await fetch(server_URL, requestOptions);
const data = await response.json();
console.log(data);
console.log(data.status);
My case is different, I'm facing the CORS error in a very weird way.
My code works normally and no CORS errors, only until I added a constant:
const MY_CONST = "...";
It seems that Google Apps Script (GAS) won't allow 'const' keyword, GAS is based on ES3 or before ES5 or that kind of thing. The error on 'const' redirect to an error page URL with no CORS.
Reference:
https://stackoverflow.com/a/54413892/5581893
In case this helps all any of those people like me:
I have a .js file which contains all my utility functions, including ones which call a GAS. I keep forgetting to clear my cache when I go to test updates, so I'll often get this kind of error because the cached code is using the /dev link instead of the /exec one.
I have a JS/HTML5 Project based on angularjs where I protect the api with an authorization token set in the http header. Now I also want to protect the access to images from the server.
I know how to do it on the server side, but how can I add HTTP Headers to image requests in angular or javascript? For api request we have already added it to the services ($ressource) and it works.
In Angular 1.2.X
There are more than a few ways to do this. In Angular 1.2, I recommend using an http interceptor to "scrub" outgoing requests and add headers.
// An interceptor is just a service.
app.factory('myInterceptor', function($q) {
return {
// intercept the requests on the way out.
request: function(config) {
var myDomain = "http://whatever.com";
// (optional) if the request is heading to your target domain,
// THEN add your header, otherwise leave it alone.
if(config.url.indexOf(myDomain) !== -1) {
// add the Authorization header (or custom header) here
config.headers.Authorization = "Token 12309123019238";
}
return config;
}
}
});
app.config(function($httpProvider) {
// wire up the interceptor by name in configuration
$httpProvider.interceptors.push('myInterceptor');
});
In Angular 1.0.X
If you're using Angular 1.0.X, you'll need to set the headers more globally in the common headers... $http.defaults.headers.common.Authentication
EDIT: For things coming from
For this you'll need to create a directive, and it's probably going to get weird.
You'll need to:
Create a directive that is either on your <img/> tag, or creates it.
Have that directive use $http service to request the image (thus leveraging the above http interceptor). For this you're going to have to examine the extension and set the proper content-type header, something like: $http({ url: 'images/foo.jpg', headers: { 'content-type': 'image/jpeg' }).then(...)
When you get the response, you'll have to take the raw base64 data and set the src attribute of your image element to a data src like so: <img src="data:image/jpeg;base64,9hsjadf9ha9s8dfh...asdfasfd"/>.
... so that'll get crazy.
If you can make it so your server doesn't secure the images you're better off.
As said here you can use angular-img-http-src (bower install --save angular-img-http-src if you use Bower).
If you look at the code, it uses URL.createObjectURL and URL.revokeObjectURL which are still draft on 19 April 2016. So look if your browser supports it.
In order to use it, declare 'angular.img' as a dependency to your app module (angular.module('myapp', [..., 'angular.img'])), and then in your HTML you can use http-src attribute for <img> tag.
For example: <img http-src="{{myDynamicImageUrl}}">
Of course, this implies that you have declared an interceptor using $httpProvider.interceptors.push to add your custom header or that you've set statically your header for every requests using $http.defaults.headers.common.MyHeader = 'some value';
we have the same issue and solve it using a custom ng-src directive ..
basically a secure-src directive which does exactly what ng-src does (its a copy basically) BUT it extends the url with a query parameter which includes the local authentication header.
The server code returning the resources are updated to not only check the header but also the query parameters. of course the token added to the query parameter might be authenticated slightly differently.. e.g. there might be a time window after after a normal rest request in which such a request is allowed etc .. since the url will remain in the browser history.
From oficial documentation at: http://docs.angularjs.org/api/ngResource/service/$resource
Usage
$resource(url, [paramDefaults], [actions]);
[...]
actions: Hash with declaration of custom action that should extend the
default set of resource actions. The declaration should be created in
the format of $http.config:
{action1: {method:?, params:?, isArray:?, headers:?, ...},
action2: {method:?, params:?, isArray:?, headers:?, ...}, ...}
More about $http service:
http://docs.angularjs.org/api/ng/service/$http#usage_parameters
As pointed out Here FIrefox supports httpChannel.setRequestHeader :
// adds "X-Hello: World" header to the request
httpChannel.setRequestHeader("X-Hello", "World", false);
In the example code above we have a variable named httpChannel which
points to an object implementing nsIHttpChannel. (This variable could
have been named anything though.)
The setRequestHeader method takes 3 parameters. The first parameter is
the name of the HTTP request header. The second parameter is the value
of the HTTP request header. And for now, just ignore the third
parameter, and just always make it false.
However this seems to be only available on Firefox (link)
You can either use Cookies to pass the value and retrieve it as a cookie instead of a HttpHeader or use ajax to retrieve the image with a custom header.
More links :
link1
link2
So I have a bit of a problem. When I ask MooTools to send a request it comes back as failed every time. I can't seem to diagnose the problem either because if I try to get the returned header info the console just gives me "Refused to get unsafe header 'Status'" Message. The only thing I can think of is that the server isn't letting me access outside resources but maybe I just coded it wrong.
Here's the request code:
var finfo = current.textFontData();
var url = 'http://antiradiant.com/clients/TMW/rbwizard/mailer.php?s='+current.size+'&b='+current.box+'&l='+current.lidWood+'&c='+current.cartID+'&f='+finfo.font+'&l1='+finfo.line1+'&l2='+finfo.line2;
console.log(url);
var req = new Request({
url: url,
onSuccess: function() {
console.log('success');
//atc2.send();
},
onFailure: function() {
console.log('failure');
console.log(this.getHeader('Status'));
//atc2.send();
},
onException: function(headerName, value) {
console.log('exception');
console.log(headerName+': '+value);
}
});
req.send();
This code is derived from the resource rb_wizard.js (lines 81-103) on http://tylermorriswoodworking.myshopify.com/pages/recipe-box-wizard?b=maple&l=cherry&s=3x5&c=42042892
Mootools has a class called Request.JSONP that will help with your cross domain problem. Its sub class of the Request class, so your methods should work the same. I believe you need to call .post() or .get() at the end instead of send, but thats about all that should chnge. I'm not sure what version you're running on but here is the link tot he docs Mootools Request.JSONP
The error message "Refused to get unsafe header 'Status'" is spat out by WebKit based browsers (Safari, Chrome, etc) when you violate the cross-domain security model.
Therefore, it seems likely that the code you pasted is located on a domain other than antiradiant.com, and therefore is not allowed (by the browser) to request sites on antiradiant.com.
What I ended up doing was just using an iframe. All I really had to do was send data to another site and not receive any so it worked out.