How can I enable cross-site $resource calls ?

30,243 views
Skip to first unread message

Johno Scott

unread,
Aug 29, 2012, 10:01:28 PM8/29/12
to ang...@googlegroups.com
My html5 mobile app is running locally on a file:// URL on Android (using PhoneGap) so any call to a remote web service is going to be cross-domain.

When I use $resource ( service in module ngResource ) to attempt a call to a remote endpoint :

            .factory('Order', ['$resource', '$http',
                function($resource, $http) { 
                    return $resource('http://example.com/api/orders/:_id', {}, {});
                }
            ])

I get an error like this :

XMLHttpRequest cannot load http://example.com/api/orders/ . Origin file:// is not allowed by Access-Control-Allow-Origin.

.. which I realise is your typical XHR cross-site security error.

Is there an option to enable cross-domain support and relax this restriction ?


Johno Scott

unread,
Aug 29, 2012, 11:41:01 PM8/29/12
to ang...@googlegroups.com
I see there is a pull request on Github for a new option



But when I change the http url to my rest endpoint it doesnt work.

Ricardo Bin

unread,
Aug 30, 2012, 9:49:32 AM8/30/12
to ang...@googlegroups.com
Hey Johno!

How i said at github, your response needs to have some header values to accept CORS, like Access-Control-Allow-Origin

Will Kriski

unread,
Aug 30, 2012, 9:58:10 AM8/30/12
to ang...@googlegroups.com
That response would have to come from the server. He wants to do it completely offline (no server).

Ricardo Bin

unread,
Aug 30, 2012, 1:33:03 PM8/30/12
to ang...@googlegroups.com
Unfortunately this header settings are required to CORS works (independent of angular $resource).

James Wright

unread,
Aug 30, 2012, 8:48:56 PM8/30/12
to ang...@googlegroups.com
an alternative is to just $http.jsonp instead of the resource.

works well for us in our phonegap app.

jamey

ad...@johnoscott.com

unread,
Aug 31, 2012, 2:00:02 AM8/31/12
to ang...@googlegroups.com
Hi Ricardo, I do have control of the server so will look at adding the headers. Thanks
Sent via BlackBerry® from Vodafone

From: Ricardo Bin <ricar...@gmail.com>
Date: Thu, 30 Aug 2012 06:49:32 -0700 (PDT)
Subject: [AngularJS] Re: How can I enable cross-site $resource calls ?
--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular?hl=en.
 
 

Johno Scott

unread,
Aug 31, 2012, 4:40:31 AM8/31/12
to ang...@googlegroups.com
Hi Will, I actually do want to call a remote web service. The issue for me is that the web app is being hosted locally from the PhoneGap app on a file:// url. So even though its my remote server (that I can control) I can't make a $http or $resource call to it because its a cross-site XHR request. Seems I need to add some headers to my server's response which I will do and see it that works.

Johno Scott

unread,
Aug 31, 2012, 4:42:56 AM8/31/12
to ang...@googlegroups.com
Hi James, can you elaborate ? Why is $http.jsonp better ? No security restrictions ? Can you provide an example ?

Will Kriski

unread,
Aug 31, 2012, 7:41:18 AM8/31/12
to ang...@googlegroups.com
This might be more of a phonegap issue then - apparently you can whitelist certain domains:

Will

Ricardo Bin

unread,
Aug 31, 2012, 10:57:56 AM8/31/12
to ang...@googlegroups.com
JSONP is very good to retrieve data between different domains.

But if you are using $resource, probably you need some RESTful methods, and jsonp cant provide this (only GET)

Johno Scott

unread,
Sep 4, 2012, 9:02:16 PM9/4/12
to ang...@googlegroups.com
Ricardo,

I managed to configure my server (running Node.js) for CORS and my PhoneGap app can now do cross-site requests and posts to the RESTful web service on it.

There was nothing I needed to do different on the client end in my Angular app and I realised that your pull-request was for a change in how Angular handles ie8 only - I didnt realise that until now - duh!

So there I was thinking that Angular didn't support CORS out of the box - but it does. You just need your server configured for CORS and that was what I needed to learn about and implement.

So for anyone else having a similar problem, here is how I solved it on Node.js server app :

/*
 * CORS Support in my Node.js web app written with Express
 */

// http://stackoverflow.com/questions/7067966/how-to-allow-cors-in-express-nodejs
app.all('/*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", 'Content-Type, X-Requested-With');
    res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    next();
});
// handle OPTIONS requests from the browser
app.options("*", function(req,res,next){res.send(200);});

// routes follow below

... and here are some useful links :

CORS - Cross Origin Resource Sharing

Good introduction to CORS (the w3 page is way too dry :)

One solution for CORS in Node.js

Know that ie8 handles CORS in a proprietary way with its own XDomainRequest object that Angular currently does not support (but Ricardo's pull-request fixes)

More CORS examples 

DjebbZ

unread,
Sep 10, 2012, 11:04:30 AM9/10/12
to ang...@googlegroups.com
Thanks Johno.

I have a similar problem though. I have no control on the server (it's an external API provider), and making requests with $resource.query() produces the same error : 

XMLHttpRequest cannot load  http://example.com/api/orders/ . Origin file:// is not allowed by Access-Control-Allow-Origin.

The thing I noticed is that when I issue the request with jQuery's $.getJSON, it works fine. Looking at the headers in Chrome Dev Tools, I see that my Angular's $resource first issues an OPTIONS request, the typical preflight request. But not jQuery, which just issues a GET request. Could someone help me understand ? My understanding from jQuery $.getJSON docs page is that $.getJSON doesn't use XMLHTTPRequests for cross-domain requests, and so isn't constrained by cross-domain policies.

I also just tried to configure $httpProvider (thanks to opensas's comments on ng.$http) by removing the custom "X-Requested-With" header. It does remove the preflight OPTIONS request and directly issues a GET request, but with no help, the same error appears. Seems like it's because ng.$resource is tied to XMLHTTPRequest.

Can someone knowledgeable help me understand ?

ps : I wanted to read Angular's source code, but I don't locally have a copy (using http://code.angular..., I'm just playing and building a demo), and Github is down...

DjebbZ

unread,
Sep 10, 2012, 12:10:11 PM9/10/12
to ang...@googlegroups.com
Got it, but without understanding.

I refactored my code so that it doesn't use $resource but $http.get. Same CORS error. Then I just replaced $http.get by $http.jsonp, as James Wright said above, without even adding an empty JSON_CALLBACK parameters, and it worked like a charm.

I think I need to fully understand this JSONP thing (somehow noob here), but it feels strange to workaround $http.get when you just want to GET... Explanations really appreciated.

AA

unread,
Jan 16, 2013, 3:56:20 PM1/16/13
to ang...@googlegroups.com, khalid....@gmail.com
Bump...I got it to work with $http.JsonP but I'm not entirely sure how or why.

DjebbZ or Johno, did you guys figure out why/how it worked for you?

Pawel Kozlowski

unread,
Jan 16, 2013, 3:57:42 PM1/16/13
to ang...@googlegroups.com
You need to a configured / cooperating server to have CORS request working correctly.


Cheers,
Pawel


On Wed, Jan 16, 2013 at 9:56 PM, AA <s.ali...@gmail.com> wrote:
ump...I got it to work with $http.JsonP but I'm not entirely sure how or why.

DjebbZ or Johno, did you guys figure out why/how it worked for you?




--
Question? Send a fiddle (http://jsfiddle.net/pkozlowski_opensource/Q2NpJ/) or a plunk (http://plnkr.co/)
Need help with jsFiddle? Check this: http://pkozlowskios.wordpress.com/2012/08/12/using-jsfiddle-with-angularjs/

Looking for UI widget library for AngularJS? Here you go: http://angular-ui.github.com/

Abrham Smith

unread,
Jan 15, 2014, 11:28:36 AM1/15/14
to ang...@googlegroups.com
Johno, I read through this discussion looking for an answer to your same question and actually found the answer.  A lot of people here gave you the run around and suggested other solutions rather than answering your original question which can be quite annoying.

This is how you will want to form your angular for resource.

angular.module('lookupService', ['ngResource']).config(function($httpProvider){delete $httpProvider.defaults.headers.common['X-Requested-With'];})
    .factory('lookupService', function ($resource) {
        return $resource(lookupURL);
    });

You will also need a CORS FILTER on the server side.  I can show you how I do it for my Java Rest Services.

I have attached a JAVA Cors Filter class that I use.

In your Web.xml you will want to add an <init-param> like the following.

<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>package.ResponseCorsFilter</param-value>
</init-param>

The param-value is just the ResponseCorsFilter class location.

That should get you where you want to be. Cheers -Abe


On Wednesday, August 29, 2012 10:01:28 PM UTC-4, Johno Scott wrote:
ResponseCorsFilter.java
Message has been deleted

David Lypka

unread,
Jul 27, 2014, 12:57:20 PM7/27/14
to ang...@googlegroups.com
I have a simple workaround which works for Angular.js within PhoneGap 3:

If your development machine such as a laptop is online through a cable modem or wireless network,
just do ipconfig, get the preferred IP, and then make your server run on that IP, with any port you wish.

Then the $resource call will work.  ***** $resource  just seems to hate 127. anything. ****
(I previously tried serving on 127.0.0.2 (suspecting only 127.0.0.1 is the issue) but still not joy...)

For example my laptop is connected on line through my AT&T private Hot Spot

So I did.
C:>ipconfig

Windows IP Configuration

Ethernet adapter Local Area Connection:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :

Wireless LAN adapter Wireless Network Connection:

   Connection-specific DNS Suffix  . : lan
   Link-local IPv6 Address . . . . . : fe80::1d55:b740:cf2f:b19a%13
   IPv4 Address. . . . . . . . . . . : 192.168.1.40
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.1.1

So I started up my python web2py backend development server on my laptop (based on the Rocket python server) to server at http://192.168.1.40:8080
(It is actually web2py in the Google App Engine dev_appserver)

Now this $resource call works for me from my Android app:

     var getResource = $resource('http://192.168.1.40:8080/init/:id', { id: '@id'}, {
          get: { method: 'GET' },
          query: { method: 'GET', isArray: true} 
      });

Result in Chrome Debugger:

XHR finished loading: "file:///android_asset/www/views/ViewPerson.html". vendor.89a83346.js:4

Here is the XHR Detail:
Request Method:GET
Status Code:303 OK
Request Headersview source
Accept:application/json, text/plain, */*
Cache-Control:no-cache
Pragma:no-cache
User-Agent:Mozilla/5.0 (Linux; Android 4.4.2; Android SDK built for x86 Build/KK) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
Query String Parametersview sourceview URL encoded
id:1
Response Headersview source
Cache-Control:no-cache
Content-Length:67
Content-Type:text/html; charset=UTF-8
Date:Sun, 27 Jul 2014 16:25:08 GMT
Expires:Fri, 01 Jan 1990 00:00:00 GMT
Location:/init/default/wiki/main
Server:Development/1.0

NOTE: As you can see, there is no issue of any 'X-Requested-With' header. I did not need any code to deal with it because it is not there fortunately.

NOTE: My web2py server code has routing logic which maps http://192.168.1.40:8080/init?id=1 to "http://192.168.1.40:8080/init/default/wiki/main

Cheers!
Reply all
Reply to author
Forward
0 new messages