Autoriser la requête CORS REST à une application Express / Node.js sur Heroku

97

J'ai écrit une API REST sur le framework express pour node.js qui fonctionne pour les demandes de la console js dans Chrome, et la barre d'URL, etc. J'essaie maintenant de la faire fonctionner pour les demandes d'une autre application, sur un autre domaine (CORS).

La première requête, faite automatiquement par le frontal javascript, est vers / api / search? Uri =, et semble échouer sur la requête OPTIONS "Preflight".

Dans mon application express, j'ajoute des en-têtes CORS, en utilisant:

var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');

    // intercept OPTIONS method
    if ('OPTIONS' == req.method) {
      res.send(200);
    }
    else {
      next();
    }
};

et:

app.configure(function () {
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(allowCrossDomain);
  app.use(express.static(path.join(application_root, "public")));
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

Depuis la console Chrome, j'obtiens ces en-têtes:

URL de la requête: http: //furious-night-5419.herokuapp.com/api/search? Uri = http% 3A% 2F% 2Flocalhost% 3A5000% 2Fcollections% 2F1% 2Fdocuments% 2F1

Méthode de demande: OPTIONS

Code d'état: 200 OK

Demander des en-têtes

Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:origin, x-annotator-auth-token, accept
Access-Control-Request-Method:GET
Connection:keep-alive
Host:furious-night-5419.herokuapp.com
Origin:http://localhost:5000
Referer:http://localhost:5000/collections/1/documents/1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5

Paramètres de chaîne de requête

uri:http://localhost:5000/collections/1/documents/1

En-têtes de réponse

Allow:GET
Connection:keep-alive
Content-Length:3
Content-Type:text/html; charset=utf-8
X-Powered-By:Express

Cela ressemble-t-il à un manque d'en-têtes appropriés envoyés par l'application API?

Merci.

Jamie Folsom
la source
J'obtiens cette erreur dans un code que je n'ai pas écrit, mais je ne comprends pas la nécessité d'un gestionnaire pour la OPTIONSméthode. Quelqu'un pourrait-il m'aider à comprendre pourquoi ne pas gérer uniquement la POSTméthode au lieu de gérer les deux POST et la OPTIONS méthode?
Ulysses Alves
Vous voudrez peut-être également inclure PATCHsi vous l'utiliserez au lieu de PUTmettre à jour une ressource
Danny

Réponses:

65

J'ai vérifié votre code sur une application ExpressJS propre et cela fonctionne très bien.

Essayez de déplacer votre app.use(allowCrossDomain)en haut de la fonction de configuration.

Olegas
la source
8
La raison pour laquelle il l'a fait est que vous devez l'avoir défini avant les app.use(app.router);acclamations!
Michal
Dans mon cas, le prochain POST n'est pas appelé après le renvoi de res.send (200) lorsque req.method == 'OPTIONS'. est-ce que je manque autre chose?
Aldo
2Aldo: Code nécessaire. Peut-être avez-vous oublié certains en-têtes? Si votre client n'envoie pas de POST après que votre serveur a correctement reçu une demande OPTIONS de contrôle en amont, essayez de vérifier la console des outils de développement. WebKit enregistre ce type d'erreurs dans la console de l'inspecteur Web.
Olegas
1
@ConnorLeech Très sympa. J'avais fait l'approche allowCrossDomain comme ci-dessus et j'étais fatigué de gérer tout ce mgmt d'en-tête. De plus, puisque nous n'avions besoin que de CORS pour le développement, cela n'avait aucun sens de consacrer autant de cycles à comprendre ce qui se passait. Heureux que node.js supporte facilement cela.
Steven
1
@ConnorLeech Je pense que vous devriez ajouter votre commentaire en guise de réponse ... fonctionne comme un régal, et c'est agréable et simple
drmrbrewer
4

pour prendre en charge les cookies avec des identifiants, vous avez besoin de cette ligne xhr.withCredentials = true;

mdn docs xhr.withCredentials

Dans Express Server, ajoutez ce bloc avant tous les autres

`app.all('*', function(req, res, next) {
     var origin = req.get('origin'); 
     res.header('Access-Control-Allow-Origin', origin);
     res.header("Access-Control-Allow-Headers", "X-Requested-With");
     res.header('Access-Control-Allow-Headers', 'Content-Type');
     next();
});`
Doron Aviguy
la source
4

J'ajoute cela comme réponse uniquement parce que le message original a été mis en commentaire et, en tant que tel, il a été ignoré par le vôtre la première fois que j'ai parcouru cette page.

Comme @ConnorLeech le souligne dans son commentaire à la réponse acceptée ci-dessus, il existe un package npm très pratique appelé, sans surprise, cors . Son utilisation est aussi simple que var cors = require('cors'); app.use(cors());(encore une fois, cribbed de la réponse de M. Leech) et peut également être appliquée de manière plus stricte et plus configurable, comme indiqué dans leurs documents .

Il peut également valoir la peine de souligner que le commentaire original auquel je fais référence ci-dessus a été fait en 2014. Nous sommes en 2019 maintenant et en regardant la page github du package npm, le dépôt a été mis à jour il y a neuf jours.

volant
la source
0

Cela ne pouvait pas être le cas pour la plupart des gens qui parcouraient cette question mais j'avais exactement le même problème et la solution n'était pas liée à CORS.

Il s'avère que le secret du jeton Web JSON stringn'a pas été défini dans les variables d'environnement, de sorte que le jeton n'a pas pu être signé. Cela a provoqué toute POSTdemande qui repose sur la vérification ou la signature d'un jeton pour obtenir un délai d'expiration et renvoyer une 503erreur, indiquant au navigateur qu'il y a quelque chose CORSqui ne va pas, ce qui n'est pas le cas. L'ajout de la variable d'environnement dans Heroku a résolu le problème.

J'espère que ça aidera quelqu'un.

ChatBrownie
la source
Oui, c'était le problème avec moi. Pourriez-vous être plus précis sur la façon dont vous avez résolu le problème. Je suis toujours en développement et je suis en cours d'exécution sur le package Express.js w / node cors.
Alan
@alan J'utilisais des promesses pour vérifier le jeton dans mon application, comment si le secret JWT n'est pas défini, la promesse ne se résoudra jamais, voici le code que j'utilisais si vous avez besoin de plus amples références.
CatBrownie
Merci d'avoir répondu. Je vais examiner le code. Par secret, je suppose que vous parlez de la deuxième clé privée. J'utilise oauth2.
Alan
J'ajouterai que toute promesse manquée produira cette erreur trompeuse! Je devais être explicite au sujet d'une version de Node utilisée, sinon mes appels à la base de données (mongo) étaient simplement suspendus et la seule chose que le navigateur pouvait me dire était 503, puis une certaine bêtise liée à Cors. Cette erreur m'a vraiment dérouté le plus longtemps que je l'avais app.use(cors());fait.
alphanumeric0101