Skip to content

IE8 CORS support #934

Closed
jsteenkamp opened this Issue May 4, 2012 · 14 comments
@IgorMinar
Angular member

http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/

in IE8 we need to use XDomainRequest:

var xdr = new XDomainRequest();
xdr.open("get", "http://www.nczonline.net/some_resource/");
xdr.onload = function(){
    //do something
};
xdr.send();
@jsteenkamp

Thanks - posted as "issue" in case you wanted to make IE8 support transparent in Angular. Not a major for me - so close if you wish.

@IgorMinar
Angular member

it seems that XDomainRequest has some limitations and jquery is hesitant to include it in the core: http://bugs.jquery.com/ticket/8283

@pkozlowski-opensource
Angular member

@IgorMinar do we want to support the XDomainRequest (IE8, 9) in the $http service? Based on @mhevery comments in #1047 it looks like no and given all the problems with XDomainRequest I would agree.

Maybe we should then close this issue and make it clear that XDomainRequest won't make it into $http?

@pkozlowski-opensource pkozlowski-opensource referenced this issue in pkozlowski-opensource/angularjs-mongolab-depreciated Dec 28, 2012
Closed

IE compatibility .query() issue #1

@makeusabrew

Hi all,

Sorry to dredge up an old thread; any further thoughts on this? Most of the group posts and other PRs just lead to dead ends.

@walski
walski commented May 6, 2013

Has anyone ever tried to use flXHR to fix this? Running into the exact same problem :cry:

@skivvies

On 2012-05-07 @IgorMinar wrote:

it seems that XDomainRequest has some limitations and jquery is hesitant to include it in the core: http://bugs.jquery.com/ticket/8283

Since this comment, someone updated that ticket with:

Even the crippled XDR can be useful if it is used by a knowledgeable developer. A jQuery team member has made an XDR ajax transport available. You must be aware of XDR limitations by reading this blog post or ask someone who has dealt with XDR problems and can mentor you through its successful use.

The XDR transport for jQuery is at: https://github.com/jaubourg/ajaxHooks/blob/master/src/xdr.js

Thought I'd post in case it's helpful to anyone watching this.

@skivvies skivvies added a commit to getlantern/www.getlantern.org that referenced this issue May 26, 2013
@skivvies skivvies show update browser prompt on IE<10
pending workaround for angular/angular.js#934
43dd0ef
@walski
walski commented May 28, 2013

I finally managed to get CORS working in IE8 and while this is definitively not for everyone here is a quick run-down on what I did:

In general I used flXHR as a Flash driven XHR drop-in replacement. We decided that it is ok to require Flash for our IE audience.

Here are the caveats I experienced during the implementation:

Flash does only support POST or GET methods

As our backend is written in Ruby the solution here was a small rack middleware that takes a FAKE-METHOD request header and then turns any POST request into the desired HTTP method.

Flash does not support empty bodies for POST methods

We built a small wrapper around flXHR that adds a dummy body whenever it was empty.

Flash does not allow you to send Authorization headers

This was a bummer as we used them for OAuth authentication. As a solution I enabled our backends to also read the token from a query param and the above mentioned flXHR wrapper just turned every Authorization header set into that query param.

IE or Flash seems to aggressively cache the AJAX responses

I just used the above mentioned wrapper to add a random seed as a query param to each request.

flXHR behaved differently on getAllResponseHeaders

It returned an array here but angular is expecting a string of all headers separated by \n.

Overall

This is far from a perfect solution but for us it was way easier than enabling JSONP across all our services. I'm happy to help anyone who runs into problems with flXHR as I spent quite some time now wrangling with it :)

The flXHR wrapper

Here is the wrapper I used to make flXHR work with angular. I just required the flXHR.js file and then this adaptor before loading angular. This is important as angular picks up the window.XMLHttpRequest on load and you can't really replace it later. Here is the raw code:

if (window.XDomainRequest !== undefined) {
  var originalXMLHttpRequest = window.originalXMLHttpRequest

  window.XMLHttpRequest = function() {
    flXHR = new flensed.flXHR();

    flXHR.autoUpdatePlayer = true;
    flXHR.xmlResponseText = false;
    flXHR.binaryResponseBody = true;

    var originalGetAllResponseHeaders = flXHR.getAllResponseHeaders;
    flXHR.getAllResponseHeaders = function() {
      return originalGetAllResponseHeaders().join("\n")
    };

    var originalOpen = flXHR.open;
    flXHR.open = function() {
      this.__angular_flxhr_request_headers = [];
      var method = arguments[0];
      if (method !== 'POST' && method !== 'GET') {
        flXHR.setRequestHeader('FAKE-METHOD', method)
        method = 'POST';
        arguments[0] = method;
      }
      this.__angular_flxhr_adaptor_http_method = method;
      this.__angular_flxhr_original_arguments = arguments;
    }

    var originalSetRequestHeader = flXHR.setRequestHeader;
    flXHR.setRequestHeader = function(header, value) {
      if (header === 'Authorization' && value !== undefined && value.match !== undefined && value.match(/^Bearer /)) {
        // Turn Authorization header into query param
        var newArguments = this.__angular_flxhr_original_arguments;
        var url = newArguments[1];
        if (url.match(/\?/)) {
          url = url + "&";
        } else {
          url = url + "?";
        }
        url = url + "oauth_token=" + value.replace(/^Bearer /, '');
        this.__angular_flxhr_original_arguments[1] = url;
      } else {
        this.__angular_flxhr_request_headers.push([header, value])
      }
    }

    var originalSend = flXHR.send;
    flXHR.send = function() {
      var newArguments = this.__angular_flxhr_original_arguments;
      var url = newArguments[1];
      if (url.match(/\?/)) {
        url = url + "&";
      } else {
        url = url + "?";
      }
      var randomSeed =  Math.floor((1 + Math.random()) * 0x1000000).toString(16) + (new Date().getTime().toString());
      url = url + "_id_random_seed=" + randomSeed;
      this.__angular_flxhr_original_arguments[1] = url;

      originalOpen.apply(this, this.__angular_flxhr_original_arguments);
      var headersToSet = this.__angular_flxhr_request_headers;
      for (var i = 0; i < headersToSet.length; i++) {
        originalSetRequestHeader.call(this, headersToSet[i][0], headersToSet[i][1]);
      }

      if (this.__angular_flxhr_adaptor_http_method === 'POST' && (arguments[0] === undefined || arguments[0] === null || arguments[0] === "")) {
        flXHR.setRequestHeader("Content-Type", "application/json");
        arguments[0] = '{}';
      }

      flXHR.setRequestHeader("Content-Type", "application/json");

      originalSend.apply(this, arguments);
    }

    return flXHR;
  }
}
@sudodoki
sudodoki commented Sep 3, 2013

Used this snippet, but there's some problem with resolving relative paths, that're not starting with slash, i.e. : 'views/main.template.html' is resolved to '@hostname/views/main.template.html' instead '@hostname/@pathname/views/main.template.html', so in case you're using this, added

if (url.match(/^[^\/\s(http)]/)) {
  url = window.location.pathname + url;
}

at line 60-62 in "send" function.
Putting this here in case somebody else runs into this problem.

@IgorMinar
Angular member

thanks @walski and @sudodoki

we are definitely not going to fix this in angular but your workaround might be useful for some folks.

@IgorMinar IgorMinar closed this Jan 5, 2014
@blaise-io

If someone was referred here by a search engine: I managed to implement CORS in IE9 and below using https://github.com/jpillora/xdomain, which is a straight-forward implement and it didn't require any modifications to Angular in our project.

@turbidwater

I ended up here, and the solution I eventually used was the jsonp method of $http and using the Yahoo APIs. That got me past CORS issues in IE9
var crossdomainURL = 'http://query.yahooapis.com/v1/public/yql?q=';
crossdomainURL += encodeURIComponent('select * from rss where url=\"' + url + '\"');
crossdomainURL += '&format=json&callback=JSON_CALLBACK';
$http.jsonp( crossdomainURL )
.success( function( data, status, headers, config ){
var resultAsJSON = data.query.results.item;
})
.error( function( data, status, headers, config ){
console.log( '!!! yahoo service error', data, status, headers, config );
});
}

@JogoShugh

+1 on https://github.com/jpillora/xdomain. This really helped in a pinch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.