I'm creating a simple app using AngularJS that displays the current spot rate (price) of Bitcoin on Coinbase using the Coinbase API.
The app works as expected in Chrome, Safari, Firefox, & Opera, however in Chrome Canary & IE I receive the following error:
Refused to execute script from 'https://coinbase.com/api/v1/prices/spot_rate?callback=angular.callbacks._0' because its MIME type ('application/json') is not executable, and strict MIME type checking is enabled.
I'm familiar with AngularJS and I've used $http service to build other apps accessing vendor API's and I've not experienced this issue.
The code below should get the spot rate via the Coinbase API, pass the data to the scope as part of the $http service callback, and refresh the value stored in the scope by making subsequent calls every 60 seconds.
angular.module('CoinbaseApp').controller('MainCtrl', function ($scope, $http, $interval) {
$scope.getPrice = function(){
$http.jsonp('https://coinbase.com/api/v1/prices/spot_rate?callback=JSON_CALLBACK').success(function(data){
$scope.data = data;
});
};
$scope.getPrice();
$interval($scope.getPrice, 60000);
});
My question: is the strict MIME type checking issue a problem with how Coinbase is serving the json? Or is it an issue with AngularJS $http service and/or how I'm requesting the data?
When calling out to a service that doesn't respond with appropriate CORS headers and does not directly support JSONP, you can install an http request interceptor to rewrite the request as a GET https://jsonp.afeld.me/, moving the original URL into the config.params (along with a callback). Then define responseTransform to simply extract and return the embedded JSON:
var app = angular.module('jsonp-proxy-request-interceptor', []);
app.service('jsonpProxyRequestInterceptor',
function JsonpProxyRequestInterceptor($log) {
var callbackRx = /callback\((.+)\);/gm;
this.request = function(config) {
if (config.url === 'https://api.domain.com/' && config.method === 'GET') {
var apiUrl = config.url;
config.url = 'https://jsonp.afeld.me/';
config.params = angular.extend({}, config.params || {}, {
url: apiUrl,
callback: 'callback'
});
config.transformResponse.unshift(function(data, headers) {
var matched = callbackRx.exec(data);
return matched ? matched[1] : data;
});
}
return config;
};
});
app.config(['$httpProvider', function configHttp($httpProvider) {
$httpProvider.interceptors.push('jsonpProxyRequestInterceptor');
}]);
You can also fork a gist of this example from https://gist.github.com/mzipay/69b8e12ad300ecaa467a.