web-dev-qa-db-fra.com

Access-Control-Allow-Origin et Angular.js $ http

Chaque fois que je crée une application Web et que je rencontre un problème avec la SCRO, je commence à préparer du café. Après avoir vissé avec pendant un moment, je parviens à le faire fonctionner mais cette fois ce n'est pas et j'ai besoin d'aide.

Voici le code côté client:

$http({method: 'GET', url: 'http://localhost:3000/api/symbol/junk', 
            headers:{
                'Access-Control-Allow-Origin': '*',
                'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
                'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With',
                'X-Random-Shit':'123123123'
            }})
        .success(function(d){ console.log( "yay" ); })
        .error(function(d){ console.log( "nope" ); });

Le côté serveur est un node.js normal avec une application express. J'ai une extension appelée cors et elle est utilisée avec express de cette façon:

var app = express();
app.configure(function(){
  app.use(express.bodyParser());
  app.use(app.router);
  app.use(cors({Origin:"*"}));
});
app.listen(3000);

app.get('/', function(req, res){
    res.end("ok");
});

Si je fais

curl -v -H "Origin: https://github.com" http://localhost:3000/

Il revient avec:

* Adding handle: conn: 0x7ff991800000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7ff991800000) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 3000 (#0)
*   Trying ::1...
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:3000
> Accept: */*
> Origin: https://github.com
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Date: Tue, 24 Dec 2013 03:23:40 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
<
* Connection #0 to Host localhost left intact
ok

Si je lance le code côté client, cela provoque cette erreur:

OPTIONS http://localhost:3000/api/symbol/junk No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. angular.js:7889
XMLHttpRequest cannot load http://localhost:3000/api/symbol/junk. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. localhost/:1
nope 

Vérification des en-têtes de Chromes:

Request URL:http://localhost:3000/api/symbol/junk
Request Method:OPTIONS
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,es;q=0.6,pt;q=0.4
Access-Control-Request-Headers:access-control-allow-Origin, accept, access-control-allow-methods, access-control-allow-headers, x-random-shit
Access-Control-Request-Method:GET
Cache-Control:max-age=0
Connection:keep-alive
Host:localhost:3000
Origin:http://localhost:8000
Referer:http://localhost:8000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Response Headersview source
Allow:GET
Connection:keep-alive
Content-Length:3
Content-Type:text/html; charset=utf-8
Date:Tue, 24 Dec 2013 03:27:45 GMT
X-Powered-By:Express

En vérifiant les en-têtes de requête, je constate que ma chaîne de test X-Random-Shit est présente dans les "en-têtes de demande de contrôle d'accès", mais que sa valeur n'y figure pas. De plus, dans ma tête, je m'attendais à voir une ligne pour chacun des en-têtes que je suis en train de définir, pas une goutte.

MISES À JOUR ---

J'ai changé mon interface en jQuery au lieu de Angular et ai créé mon backend comme ceci:

var app = express();
app.configure(function(){
  app.use(express.bodyParser());
  app.use(app.router);
});
app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header('Access-Control-Allow-Methods', 'OPTIONS,GET,POST,PUT,DELETE');
    res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
    if ('OPTIONS' == req.method){
        return res.send(200);
    }
    next();
});

app.get('/', function(req, res){
    res.end("ok");
});

Maintenant, cela fonctionne avec GET mais pas avec autre chose (PUT, POST ..).

Je verrai si l'un de vous propose une solution. En attendant, jetter le concept RESTful par la fenêtre et tout faire avec GETs.

31
PCoelho

Je suis nouveau sur AngularJS et je suis tombé sur ce problème de la SCRO, j'ai presque perdu la tête! Heureusement, j'ai trouvé un moyen de résoudre ce problème. Alors voilà ...

Mon problème était, quand j'utilise la ressource AngularJS $ dans l'envoi de requêtes d'API, je reçois ce message d'erreur XMLHttpRequest cannot load http://website.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. Oui, j'ai déjà ajouté callback = "JSON_CALLBACK" et cela n'a pas fonctionné.

Ce que j’ai fait pour résoudre le problème, au lieu d’utiliser la méthode GET ou de recourir à $ http.get, j’ai utilisé JSONP. Il suffit de remplacer la méthode GET par JSONP et de modifier également le format de réponse de l’API en JSONP.

    myApp.factory('myFactory', ['$resource', function($resource) {

           return $resource( 'http://website.com/api/:apiMethod',
                        { callback: "JSON_CALLBACK", format:'jsonp' }, 
                        { 
                            method1: { 
                                method: 'JSONP', 
                                params: { 
                                            apiMethod: 'hello world'
                                        } 
                            },
                            method2: { 
                                method: 'JSONP', 
                                params: { 
                                            apiMethod: 'hey ho!'
                                        } 
                            }
            } );

    }]);

J'espère que quelqu'un trouvera cela utile. :)

8
Wreeecks

J'ai eu du succès avec express et en éditant le res.header. Le mien correspond très bien au vôtre, mais j'ai un Allow-Headers Différent, comme indiqué ci-dessous:

res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

J'utilise aussi Angular et Node/Express, mais les en-têtes ne sont pas appelés dans le code Angular, mais uniquement dans le nœud/express.

4
wnordmann

Ecrire ce middleware pourrait aider!

app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With,     Content-Type, Accept");
next();
});

pour plus de détails, visitez http://enable-cors.org/server_expressjs.html

1
Vickar

@ Swapnil Niwane

J'ai pu résoudre ce problème en appelant une requête ajax et en formatant les données en 'jsonp'.

$.ajax({
          method: 'GET',
          url: url,
          defaultHeaders: {
              'Content-Type': 'application/json',
              "Access-Control-Allow-Origin": "*",
              'Accept': 'application/json'
           },

          dataType: 'jsonp',

          success: function (response) {
            console.log("success ");
            console.log(response);
          },
          error: function (xhr) {
            console.log("error ");
            console.log(xhr);
          }
});
1
zdrohn

Ajout ci-dessous à server.js résolu le mien

    server.post('/your-rest-endpt/*', function(req,res){
    console.log('');
    console.log('req.url: '+req.url);
    console.log('req.headers: ');   
    console.dir(req.headers);
    console.log('req.body: ');
    console.dir(req.body);  

    var options = {
        Host: 'restAPI-IP' + ':' + '8080'

        , protocol: 'http'
        , pathname: 'your-rest-endpt/'
    };
    console.log('options: ');
    console.dir(options);   

    var reqUrl = url.format(options);
    console.log("Forward URL: "+reqUrl);

    var parsedUrl = url.parse(req.url, true);
    console.log('parsedUrl: ');
    console.dir(parsedUrl);

    var queryParams = parsedUrl.query;

    var path = parsedUrl.path;
    var substr = path.substring(path.lastIndexOf("rest/"));
    console.log('substr: ');
    console.dir(substr);

    reqUrl += substr;
    console.log("Final Forward URL: "+reqUrl);

    var newHeaders = {
    };

    //Deep-copy it, clone it, but not point to me in shallow way...
    for (var headerKey in req.headers) {
        newHeaders[headerKey] = req.headers[headerKey];
    };

    var newBody = (req.body == null || req.body == undefined ? {} : req.body);

    if (newHeaders['Content-type'] == null
            || newHeaders['Content-type'] == undefined) {
        newHeaders['Content-type'] = 'application/json';
        newBody = JSON.stringify(newBody);
    }

    var requestOptions = {
        headers: {
            'Content-type': 'application/json'
        }
        ,body: newBody
        ,method: 'POST'
    };

    console.log("server.js : routes to URL : "+ reqUrl);

    request(reqUrl, requestOptions, function(error, response, body){
        if(error) {
            console.log('The error from Tomcat is --> ' + error.toString());
            console.dir(error);
            //return false;
        }

        if (response.statusCode != null 
                && response.statusCode != undefined
                && response.headers != null
                && response.headers != undefined) {
            res.writeHead(response.statusCode, response.headers);
        } else {
            //404 Not Found
            res.writeHead(404);         
        }
        if (body != null
                && body != undefined) {

            res.write(body);            
        }
        res.end();
    });
});
1
Babu M

J'ai trouvé un moyen d'utiliser la méthode JSONP dans $http directement et avec le support de params dans l’objet config:

params = {
  'a': b,
  'callback': 'JSON_CALLBACK'
};

$http({
  url: url,
  method: 'JSONP',
  params: params
})
0
paradite