Permitir solicitação CORS REST para um aplicativo Express / Node.js no Heroku

97

Eu escrevi uma API REST na estrutura expressa para node.js que funciona para solicitações do console js no Chrome e barra de URL, etc. Agora estou tentando fazê-la funcionar para solicitações de outro aplicativo, em um domínio (CORS).

A primeira solicitação, feita automaticamente pelo front end javascript, é para / api / search? Uri = e parece estar falhando na solicitação de OPÇÕES de "comprovação".

Em meu aplicativo expresso, estou adicionando cabeçalhos 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();
    }
};

e:

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

No console do Chrome, obtenho estes cabeçalhos:

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

Método de solicitação: OPÇÕES

Código de status: 200 OK

Solicitar cabeçalhos

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 string de consulta

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

Cabeçalhos de resposta

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

Isso parece uma falta de cabeçalhos adequados enviados pelo aplicativo API?

Obrigado.

Jamie Folsom
fonte
Estou recebendo este erro em um código que não escrevi, mas não entendo a necessidade de um manipulador para o OPTIONSmétodo. Alguém poderia me ajudar a entender por que não lidar apenas com o POSTmétodo em vez de lidar com o método POST e OPTIONS ?
Ulysses Alves de
Também pode querer incluir PATCHse você for usá-lo em vez de PUTatualizar um recurso
Danny

Respostas:

65

Verifiquei seu código em um aplicativo ExpressJS limpo e ele funciona perfeitamente.

Tente mover app.use(allowCrossDomain)para o topo da função de configuração.

Olegas
fonte
8
O motivo disso é porque você precisa defini-lo antes do app.use(app.router);Cheers!
Michal
No meu caso, o próximo POST não está sendo chamado após o envio de res.send (200) quando req.method == 'OPTIONS'. estou perdendo mais alguma coisa?
Aldo
2Aldo: Código necessário. Pode ser que você esqueceu alguns cabeçalhos? Se o seu cliente não estiver enviando um POST depois que seu servidor receber corretamente uma solicitação OPTIONS de comprovação, tente verificar o console de ferramentas do desenvolvedor. O WebKit registra esse tipo de erro no console do inspetor da web.
Olegas
1
@ConnorLeech Muito bom. Eu estava fazendo a abordagem allowCrossDomain como acima e estava cansado de lidar com todo aquele gerenciamento de cabeçalho. Além disso, como só precisávamos do CORS para o dev, não fazia sentido dedicar tantos ciclos para descobrir o que estava acontecendo. Ainda bem que existe suporte para node.js para permitir isso facilmente.
Steven
1
@ConnorLeech Eu acho que você deveria adicionar seu comentário como uma resposta ... funciona como uma delícia, e é bom e simples
drmrbrewer
4

para suportar cookies com credenciais, você precisa desta linha xhr.withCredentials = true;

mdn docs xhr.withCredentials

No Express Server adicione este bloco antes de todos os outros

`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
fonte
4

Estou adicionando isso como uma resposta apenas porque a postagem original foi colocada como um comentário e, como tal, foi esquecido pelos meus sinceramente na primeira vez que li esta página.

Como @ConnorLeech aponta em seu comentário sobre a resposta aceita acima, há um pacote npm muito útil chamado, não surpreendentemente, cors . Seu uso é tão simples quanto var cors = require('cors'); app.use(cors());(novamente, baseado na resposta do Sr. Leech) e também pode ser aplicado de uma forma mais rígida e configurável, conforme descrito em seus documentos .

Também pode valer a pena apontar que o comentário original a que me refiro foi feito em 2014. É 2019 agora e olhando para a página github do pacote npm, o repo foi atualizado recentemente, há nove dias.

Flyingace
fonte
0

Não poderia ser o caso para a maioria das pessoas navegando nesta questão, mas eu tive exatamente o mesmo problema e a solução não estava relacionada a CORS.

Acontece que o segredo JSON Web Token stringnão foi definido nas variáveis ​​de ambiente, portanto, o token não pôde ser assinado. Isso causava qualquer POSTsolicitação que dependa da verificação ou assinatura de um token para obter um tempo limite e retornar um 503erro, informando ao navegador que há algo errado CORS, e não está. Adicionar a variável de ambiente no Heroku resolveu o problema.

Espero que isso ajude alguém.

CatBrownie
fonte
Sim, esse era o meu problema. Você poderia ser mais específico sobre como resolveu o problema. Ainda estou em desenvolvimento e estou executando o pacote Express.js w / node cors.
Alan
@alan Eu estava usando promessas para verificar o token em meu aplicativo, de alguma forma, se o segredo JWT não for definido, a promessa nunca será resolvida, aqui está o código que eu estava usando se você precisar de mais referências.
CatBrownie
Obrigado por responder. Vou examinar o código. Em segredo, presumo que você esteja falando sobre a segunda chave privada. Estou usando oauth2.
Alan
Acrescentarei que qualquer promessa falhada produzirá esse erro enganoso! Eu tive que ser explícito sobre uma versão do Node sendo usada ou então minhas chamadas de banco de dados (mongo) estavam simplesmente travadas e a única coisa que o navegador poderia me dizer era 503 e então algumas bobagens relacionadas ao Cors. Esse erro realmente me confundiu por mais tempo app.use(cors());.
alfanumérico0101