Angular CORS poly filling for XHR on IE 8 - 9

2,125 views
Skip to first unread message

Oleg Belausov

unread,
Sep 10, 2013, 3:57:47 AM9/10/13
to ang...@googlegroups.com
Hello everyone!

I have an API set up on a different sub domain than my front end server.
The problem that I am facing is that any IE version below 10 doesn't support CORS or XmlHttppRequest2.

According to this article, you can use something called XDomainRequest on IE in order to enable CORS support.

The question is, does ANgular has some kind of build in implementation for this?, how can I implement this XDomainRequest stuff within my Angular code
without ruining the structure of my code?.

Basically my question is: is there any alternative other than setting a proxy from mydomian.com/api to api.mydomain.com/

Tony pee

unread,
Sep 10, 2013, 12:20:45 PM9/10/13
to ang...@googlegroups.com
There is an outstanding pull request for this - i pulled out the changes and made this function to patch my angular. Hopefully the pr is sorted out sometime, or maybe patching is the best approach (just needs maintaining)

I just run this over my main application module. Im not sure if it breaks anything, but its working for me. 


function patchAngular (mod) {
        function noop() {}
        noop.$inject = [];
        var IS_SAME_DOMAIN_URL_MATCH = /^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.-]*)?(:([0-9]+))?(.*)$/;
        var XHR = window.XMLHttpRequest || function() {
          try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
          try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
          try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
          throw new Error("This browser does not support XMLHttpRequest.");
        }, XDR = !window.msPerformance && window.XDomainRequest || null;

        function createHttpBackend($browser, XHR, XDR, $browserDefer, callbacks, rawDocument, locationProtocol) {
          // TODO(vojta): fix the signature
          return function(method, url, post, callback, headers, timeout, withCredentials, useXDomain) {
            $browser.$$incOutstandingRequestCount();
            url = url || $browser.url();


            if (angular.lowercase(method) == 'jsonp') {
              var callbackId = '_' + (callbacks.counter++).toString(36);
              callbacks[callbackId] = function(data) {
                callbacks[callbackId].data = data;
              };

              jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
                  function() {
                    if (callbacks[callbackId].data) {
                      completeRequest(callback, 200, callbacks[callbackId].data);
                    } else {
                      completeRequest(callback, -2);
                    }
                    delete callbacks[callbackId];
                  });
             } else {
              var status;
              if (true && XDR) {
                var xdr = new XDR();
                xdr.open(method.toLowerCase(), url);

                // Required to XDomainRequest works
                xdr.timeout = timeout;
                xdr.onprogress = function() {};

                xdr.ontimeout = function() {
                  completeRequest(callback, 408, 'Timeout', 'Content-Type: text/plain');
                  xdr.abort();
                };

                xdr.onload = function() {
                  completeRequest(callback, 200, xdr.responseText, 'Content-Type: ' + xdr.contentType);
                };

                xdr.onerror = function() {
                  completeRequest(callback, 500, 'Error', 'Content-Type: text/plain');
                  xdr.abort();
                };


                $browserDefer(function () {
                  xdr.send();
                }, 0); //fix IE bug that raises '$apply already in progress' on cached requests

                if (timeout > 0) {
                  $browserDefer(function() {
                    status = -1;
                    xdr.abort();
                  }, timeout);
                }

              } else {
                var xhr = new XHR();
                xhr.open(method, url, true);

                angular.forEach(headers, function(value, key) {
                  if (value) {xhr.setRequestHeader(key, value);}
                });

                // In IE6 and 7, this might be called synchronously when xhr.send below is called and the
                // response is in the cache. the promise api will ensure that to the app code the api is
                // always async
                xhr.onreadystatechange = function() {
                  if (xhr.readyState == 4) {
                    completeRequest(
                        callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
                  }
                };

                if (withCredentials) {
                  xhr.withCredentials = true;
                }

                xhr.send(post || '');

                if (timeout > 0) {
                  $browserDefer(function() {
                    status = -1;
                    xhr.abort();
                  }, timeout);
                }

              }
            }

            function completeRequest(callback, status, response, headersString) {
              // URL_MATCH is defined in src/service/location.js
              var protocol = (url.match(IS_SAME_DOMAIN_URL_MATCH) || ['', locationProtocol])[1];

              // fix status code for file protocol (it's always 0)
              status = (protocol == 'file') ? (response ? 200 : 404) : status;

              // normalize IE bug (http://bugs.jquery.com/ticket/1450)
              status = status == 1223 ? 204 : status;

              callback(status, response, headersString);
              $browser.$$completeOutstandingRequest(noop);
            }
          };

          function jsonpReq(url, done) {
            // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
            // - fetches local scripts via XHR and evals them
            // - adds and immediately removes script elements from the document
            var script = rawDocument.createElement('script'),
                doneWrapper = function() {
                  rawDocument.body.removeChild(script);
                    if (done) {
                        done();
                    }
                };

            script.type = 'text/javascript';
            script.src = url;

            if (msie) {
              script.onreadystatechange = function() {
                if (/loaded|complete/.test(script.readyState)) doneWrapper();
              };
            } else {
              script.onload = script.onerror = doneWrapper;
            }

            rawDocument.body.appendChild(script);
          }
        }

        mod.config(['$httpBackendProvider', function($httpBackendProvider) {
            $httpBackendProvider.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
            return createHttpBackend($browser, XHR, XDR, $browser.defer, $window.angular.callbacks,
                $document[0], $window.location.protocol.replace(':', ''), true);
          }];
        }]);
    };


--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/groups/opt_out.



--
Tony Polinelli

ThomasBurleson

unread,
Sep 10, 2013, 3:14:36 PM9/10/13
to ang...@googlegroups.com
Check out XDomain on GitHub.

- Thomas

Olex Lapshyn

unread,
Sep 13, 2013, 6:14:21 AM9/13/13
to ang...@googlegroups.com
We used flXHR and some code snippet found in angular.js issue list
https://github.com/angular/angular.js/issues/934

Emanuele Fortunati

unread,
Jul 23, 2014, 6:49:22 AM7/23/14
to ang...@googlegroups.com
This solution worked perfectly for me, thank you very much!

As a side note remember to add headers to your backend server otherwise it won't be working anyway

In my Node.js + Express.js I've done something like

app.post('/url', function (req, res) {

    res.header('Access-Control-Allow-Origin', ALLOWED_DOMAINS);
    res.header('Access-Control-Allow-Methods', 'POST');
    res.header('Access-Control-Allow-Headers', 'Content-Type');

    res.send(data);

});
Reply all
Reply to author
Forward
0 new messages