AngularJS: POST Data to External REST API

28,368

Solution 1

Your two problems are actually one problem. The OPTIONS request is part of the CORS process. For POST requests, the browser first sends an OPTIONS call, and the server responds if it is okay to execute it.

If the OPTIONS request fails, Angular / Chrome shows you the reason in the console. For example:

OPTIONS https://*** Request header field Content-Type is not allowed by Access-Control-Allow-Headers. angular.min.js:106

XMLHttpRequest cannot load https://***. Request header field Content-Type is not allowed by Access-Control-Allow-Headers. 

You probably have to set Access-Control-Allow Headers on the server, too:

header('Access-Control-Allow-Headers: Content-Type, x-xsrf-token')

x-xrsf-token is for angular' to prevent CSRF. You may have to add more headers, depending on what you send from the client.

Here is a very good guide on CORS: https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS

Solution 2

In AngularJS to make CORS working you also have to overwrite default settings of the angular httpProvider:

var myApp = angular.module('myApp', [
    'myAppApiService']);

myApp.config(['$httpProvider', function($httpProvider) {
        $httpProvider.defaults.useXDomain = true;
        delete $httpProvider.defaults.headers.common['X-Requested-With'];
    }
]);

Just setting useXDomain to true is not enough. AJAX request are also send with the X-Requested-With header, which indicate them as being AJAX. Removing the header is necessary, so the server is not rejecting the incoming request.

Note: Answer only works for older AngularJS version previous to 1.2. With 1.2 and above you don't have to do do anything to enable CORS.

Share:
28,368
Lucas Raines
Author by

Lucas Raines

Updated on May 31, 2020

Comments

  • Lucas Raines
    Lucas Raines almost 4 years

    I have a basic AngularJS service setup like so:

    app.factory('User', function($resource) {
    return $resource('http://api.mysite.com/user/:action:id/:attr', {}, {
        history: {
            method: 'GET',
            params: {
                attr: 'history'
            }
        },
        update: {
            method: 'POST',
            params: {
                name: 'test'
            }
        }
    });
    });
    

    and I use it like this:

    User.history({id: 'testID'}, function(data) {
        console.log('got history');
        console.log(data);
    });
    User.update({id: 'me'}, function(data) {
        console.log('updated');
        console.log(data);
    });
    

    Problem one: User.update(), despite having the method set to POST, keeps sending OPTIONS as the request method.

    Though Chrome Dev tools reports the request header Access-Control-Request-Method:POST is sent as well (Not sure if that means anything).

    Problem two: I keep getting an error with CORS, despite having these headers set in the API code:

    header('Access-Control-Allow-Origin: *');
    header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS");
    

    This problem only shows up though if making a non-GET request.

    What's the proper way to be handling this? I've also looked into JSONP, but with this being a RESTful api, I'm not sure how to get around the problems with only GET support.

  • Lucas Raines
    Lucas Raines almost 11 years
    The browser sending the OPTIONS request then the POST request was where my problem was. The server wasn't set up to respond to the initial OPTIONS. I didn't need to do anything with x-xsrf-token though. Used the code from here to handle all the necessary headers: stackoverflow.com/questions/13293157/…
  • Lucas Raines
    Lucas Raines almost 11 years
    This actually wasn't necessary, as I have access to the api server and added header('Access-Control-Allow-Headers: X-Requested-With'); which I assume fixes the problem you're talking about.
  • Torsten Engelbrecht
    Torsten Engelbrecht almost 11 years
    Good tip. I wasn't aware of setting the allow headers header. Thx.
  • JasonS
    JasonS over 10 years
    this was exactly my problem. thank you. If you use node/express, follow the "enable pre-flight" instructions here to get cors working: github.com/troygoode/node-cors
  • JasonS
    JasonS over 10 years
    maybe you had to do this with old angular versions, but I know that in angular 1.2.x, you do not need to do this. Following @Narretz's answer works for me.
  • Benjamin Gruenbaum
    Benjamin Gruenbaum about 10 years
    @TorstenEngelbrecht useXDomain is an option from a rejected pull request that never existed, doing useXDomain = true will accomplish nothing.
  • Torsten Engelbrecht
    Torsten Engelbrecht about 10 years
    @BenjaminGruenbaum was not aware of that. I am wondering why it did not work without it to this time. Anyway, this answer is already pretty old in Angular terms, so its most likely outdated. I am not using CORS since then and never touched that area again so far.