Permitir la solicitud CORS REST a una aplicación Express / Node.js en Heroku


97

Escribí una API REST en el marco expreso para node.js que funciona para solicitudes de la consola js en Chrome y la barra de URL, etc. Ahora estoy tratando de que funcione para solicitudes de otra aplicación, en una diferente dominio (CORS).

La primera solicitud, realizada automáticamente por el front-end de JavaScript, es / api / search? Uri =, y parece fallar en la solicitud OPTIONS de "verificación previa".

En mi aplicación express, estoy agregando encabezados CORS, usando:

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();
    }
};

y:

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 }));
});

Desde la consola de Chrome obtengo estos encabezados:

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

Método de solicitud: OPCIONES

Código de estado: 200 OK

Solicitar encabezados

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

Parámetros de cadena de consulta

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

Encabezados de respuesta

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

¿Parece esto una falta de encabezados adecuados enviados por la aplicación API?

Gracias.


Recibo este error en un código que no escribí, pero no entiendo la necesidad de un controlador para el OPTIONSmétodo. Podría alguien por favor me ayude a entender por qué no manejar sólo el POSTmétodo en lugar de manejar tanto POST y OPTIONS método?
Ulysses Alves

También es posible que desee incluir PATCHsi lo usará en lugar de PUTactualizar un recurso
Danny

Respuestas:


65

Verifiqué su código en una aplicación ExpressJS limpia y funciona bien.

Intente mover su app.use(allowCrossDomain)a la parte superior de la función de configuración.


8
La razón por la que lo hizo es porque necesita tenerlo definido antes de los app.use(app.router);Cheers!
Michal

En mi caso, no se llama al siguiente POST después de devolver res.send (200) cuando req.method == 'OPTIONS'. ¿Me estoy perdiendo algo más?
Aldo

2Aldo: Código necesario. ¿Puede haber olvidado algunos encabezados? Si su cliente no envía un POST después de que su servidor recibe correctamente una solicitud de OPCIONES de verificación previa, intente verificar la consola de herramientas de desarrollador. WebKit registra este tipo de errores en la consola del inspector web.
Olegas

1
@ConnorLeech Muy agradable. Había estado haciendo el enfoque allowCrossDomain como arriba y estaba cansado de lidiar con todo ese encabezado mgmt. Además, dado que solo necesitábamos CORS para el desarrollo, no tenía sentido dedicar tantos ciclos a averiguar qué estaba pasando. Me alegro de que haya compatibilidad con node.js para permitir esto fácilmente.
Steven

1
@ConnorLeech Creo que deberías agregar tu comentario como respuesta ... funciona como un regalo, y es agradable y simple
drmrbrewer

4

para admitir cookies con credenciales, necesita esta línea xhr.withCredentials = true;

mdn docs xhr.withCredentials

En Express Server agregue este bloque antes que todos los demás

`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();
});`

4

Estoy agregando esto como respuesta solo porque la publicación original se incluyó como un comentario y, como tal, los suyos lo pasaron por alto la primera vez que revisé esta página.

Como @ConnorLeech señala en su comentario a la respuesta aceptada anterior, hay un paquete npm muy útil llamado, como era de esperar, cors . Su uso es tan simple como var cors = require('cors'); app.use(cors());(nuevamente, extraído de la respuesta del Sr. Leech) y también se puede aplicar de una manera más estricta y configurable, como se describe en sus documentos .

También puede valer la pena señalar que el comentario original al que me refiero anteriormente se realizó en 2014. Ahora es 2019 y, al mirar la página de github del paquete npm, el repositorio se actualizó hace tan solo nueve días.


0

No podría ser el caso para la mayoría de las personas que consultan esta pregunta, pero tuve exactamente el mismo problema y la solución no estaba relacionada con CORS .

Resulta que el secreto de JSON Web Token stringno se definió en las variables de entorno, por lo que el token no se pudo firmar. Esto provocó que cualquier POSTsolicitud que se basara en verificar o firmar un token para obtener un tiempo de espera y devolver un 503error, le dice al navegador que hay algo mal enCORS , que no es así. Agregar la variable de entorno en Heroku resolvió el problema.

Espero que esto ayude a alguien.


Sí, ese era el problema conmigo. ¿Podría ser más específico cómo resolvió el problema? Todavía estoy en desarrollo y estoy ejecutando Express.js con el paquete node cors.
Alan

@alan Estaba usando promesas para verificar el token en mi aplicación, de alguna manera si el secreto de JWT no está definido, la promesa nunca se resolverá, aquí está el código que estaba usando si necesita más referencia.
CatBrownie

Gracias por responder. Revisaré el código. Por secreto, supongo que estás hablando de la segunda clave privada. Estoy usando oauth2.
Alan

¡Agregaré que cualquier promesa fallida producirá este error engañoso! Tenía que ser explícito sobre el uso de una versión de Node o, de lo contrario, mis llamadas a la base de datos (mongo) simplemente estaban colgadas y lo único que el navegador podía decirme era 503 y luego algunas tonterías relacionadas con Cors. Este error realmente me confundió durante más tiempo app.use(cors());.
alfanumérico
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.