Não 'Access-Control-Allow-Origin' o cabeçalho está presente no recurso solicitado - quando se tenta obter dados de uma API REST

I'estou tentando pegar alguns dados do REST API da HP Alm. Funciona muito bem com um pequeno script de ondulação - eu recebo meus dados.

Agora fazer isso com JavaScript, fetch e ES6 (mais ou menos) parece ser uma questão maior. Eu continuo a receber esta mensagem de erro:

Fetch API cannot load < URL>. Resposta ao pedido de pré-vôo doesn't passar a verificação de controle de acesso: No 'Access-Control-Allow-Origin' cabeçalho é presente no recurso solicitado. Origem 'http://127.0.0.1:3000' é portanto, não é permitido o acesso. A resposta tinha o código de status HTTP 501. Se uma resposta opaca atende às suas necessidades, defina o modo de pedido's para ' no-cors' para ir buscar o recurso com CORS desabilitado.

Entendo que isso é porque estou tentando obter esses dados dentro do meu local de hospedagem e a solução deveria ser usar CORS. Agora eu pensei que realmente fiz isso, mas de alguma forma ou ignora o que eu escrevo no cabeçalho ou o problema é outra coisa?

Então, há algum problema de implementação? Estou a fazer isto mal? Eu posso't verificar os logs do servidor infelizmente. I'estou realmente um pouco presa aqui.

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

Estou a usar o cromado. Eu também tentei usar o Chrome CORS Plugin, mas depois estou recebendo outra mensagem de erro:

O valor do ' Access-Control-Allow-Origin' cabeçalho na resposta não deve ser o wildcard '*' quando o modo de credenciais do pedido's é ' incluir'. Origin 'http://127.0.0.1:3000' portanto não é permitido acesso. O modo de credenciais dos pedidos iniciados pelo XMLHttpRequest é controlado pelo atributo withCredentials.

Solução

Esta resposta cobre muito terreno, por isso está dividida em três partes:

  • Como usar um proxy CORS para se locomover "No Access-Control-Allow-Origin header " problemas
  • Como evitar o pré-voo CORS
  • Como corrigir "Access-Control-Allow-Origin header must not be the wildcard " problems

    *Como usar um proxy CORS para se locomover "No Access-Control-Allow-Origin header "* Problemas*** Se você não controla o servidor para o qual o seu código JavaScript do frontend está enviando uma solicitação, e o problema com a resposta desse servidor é apenas a falta do cabeçalho necessário 'Access-Control-Allow-Origin', você ainda pode fazer com que as coisas funcionem, fazendo a solicitação através de um proxy CORS. Para mostrar como isso funciona, primeiro aqui está um código que não utiliza um proxy CORS:

const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

O motivo pelo qual o bloco catch é atingido lá é que o navegador impede que esse código acesse a resposta que vem de https://example.com. E o motivo pelo qual o navegador faz isso é que a resposta não tem o cabeçalho de resposta `Access-Control-Allow-Origin'. Agora, aqui está exatamente o mesmo exemplo, mas apenas com um proxy CORS adicionado:

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

Se o https://cors-anywhere.herokuapp.com estiver em baixo ou indisponível quando você tentar, então veja abaixo como implementar seu próprio servidor CORS Anywhere no Heroku em apenas 2-3 minutos. O segundo trecho de código acima pode acessar a resposta com sucesso porque pegar o URL do pedido e mudá-lo para https://cors-anywhere.herokuapp.com/https://example.com-by - simplesmente prefixando-o com o URL proxy - causa o pedido para ser feito através desse proxy, que então:

  1. Encaminha o pedido para `https://example.com'.
  2. Recebe a resposta de https://example.com.
  3. Adiciona o cabeçalho `Access-Control-Allow-Origin' à resposta.
  4. Passa essa resposta, com esse cabeçalho adicionado, de volta ao código frontend solicitante. O browser então permite que o código frontend acesse a resposta, porque essa resposta com o cabeçalho de resposta Access-Control-Allow-Origin é o que o browser vê. Você pode facilmente executar seu próprio proxy usando o código de https://github.com/Rob--W/cors-anywhere/.
    Você também pode facilmente implantar seu próprio proxy para Heroku em literalmente apenas 2-3 minutos, com 5 comandos:
git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

Depois de executar esses comandos, você vai acabar com seu próprio servidor CORS Anywhere rodando em, por exemplo, https://cryptic-headland-94862.herokuapp.com/. Então, ao invés de prefixar a URL do seu pedido com https://cors-anywhere.herokuapp.com', prefira-a com a URL da sua própria instância; por exemplo, https://cryptic-headland-94862.herokuapp.com/https://example.com. **Então se quando você for tentar usar https://cors-anywhere.herokuapp.com, você achar que está em baixo** (o que algumas vezes será), então considere obter uma conta Heroku (se você ainda não o fez) e leve 2 ou 3 minutos para fazer os passos acima para implantar seu próprio servidor CORS Anywhere no Heroku. Independentemente de você rodar seu próprio servidor ou utilizar https://cors-anywhere.herokuapp.com ou outro proxy aberto, esta solução irá funcionar mesmo se a requisição for uma requisição que acione os navegadores para fazer uma pré-vôo CORSOPÇÕES, pois nesse caso, o proxy também envia de volta os cabeçalhosAccess-Control-Allow-Headers' e `Access-Control-Allow-Methods' necessários para fazer a pré-vôo com sucesso.

**Como evitar o pré-voo CORS*** O código na pergunta aciona um pré-voo CORS - desde que ele envie um cabeçalho de "Autorização". https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests Mesmo sem isso, o Content-Type: application/json header também acionaria o pré-vôo. O que "preflight" significa: antes do navegador tentar o POST no código da pergunta, ele primeiro enviará uma solicitação de OPTIONS ao servidor - para determinar se o servidor está optando por receber um POST de origem cruzada que inclui os cabeçalhos Authorization e Content-Type: application/json.

Funciona muito bem com um pequeno script de ondulação - eu recebo meus dados. Para testar corretamente com o curl', você precisa emular o pedido de pré-vôoOPTIONS' que o navegador envia:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

...com https://the.sign_in.url substituído por qualquer que seja o seu verdadeiro sign_in URL. A resposta que o navegador precisa ver a partir desse pedido de OPTIONS deve incluir cabeçalhos como este:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

Se a resposta OPÇÕES não incluir esses cabeçalhos, então o navegador irá parar por aí mesmo e nunca tentará sequer enviar o pedido POST. Além disso, o código de status HTTP da resposta deve ser um 2xx-typically 200 ou 204. Se for qualquer outro código de status, o navegador vai parar por aí mesmo. O servidor na questão está respondendo à solicitação OPTIONS' com um código de status 501, o que aparentemente significa que ele está tentando indicar que não implementa suporte a solicitaçõesOPTIONS'. Outros servidores normalmente respondem com um código de status 405 "Método não permitido" neste caso. Então você nunca será capaz de fazer pedidos POST diretamente a esse servidor a partir do seu código frontend JavaScript se o servidor responder a esse pedido OPTIONS com um 405 ou 501 ou qualquer coisa que não seja 200 ou 204 ou se não responder com esses cabeçalhos de resposta necessários. A maneira de evitar o disparo de um preflight para o caso na pergunta seria:

  • se o servidor não exigisse um cabeçalho de requisição de Autorização mas, em vez disso (por exemplo) confiasse nos dados de autenticação embutidos no corpo da requisição POST ou como um parâmetro de consulta
  • se o servidor não exigisse que o corpo POST tivesse um tipo de mídia Content-Type: application/json mas, em vez disso, aceitasse o corpo POST como application/x-www-form-urlencoded com um parâmetro chamado json (ou qualquer outro) cujo valor são os dados JSON

    Como corrigir "Access-Control-Allow-Origin header must not be the wildcard " problems

    estou a receber outra mensagem de erro:

    O valor do ' Access-Control-Allow-Origin' cabeçalho na resposta não deve ser o wildcard '' quando o modo de credenciais do pedido's é ' include'. Origin 'http://127.0.0.1:3000' portanto não é permitido acesso. O modo de credenciais dos pedidos iniciados pelo XMLHttpRequest é controlado pelo atributo withCredentials. Para uma solicitação que inclui credenciais, os navegadores não permitirão que o seu código JavaScript frontend acesse a resposta se o valor do cabeçalho de resposta Access-Control-Allow-Origin for `. Em vez disso, o valor nesse caso deve corresponder exatamente à origem do seu código front-end,http://127.0.0.1:3000'. Veja Requisitos de credenciais e wildcards no artigo MDN HTTP access control (CORS). Se você controla o servidor para o qual está enviando a requisição, então uma maneira comum de lidar com este caso é configurar o servidor para pegar o valor do cabeçalho da requisição Origin', e ecoar/refletir isso de volta para o valor do cabeçalho da respostaAccess-Control-Allow-Origin'. Por exemplo, com nginx:

add_header Access-Control-Allow-Origin $http_origin

Mas isso é apenas um exemplo; outros sistemas de servidores (web) fornecem formas semelhantes de ecoar valores de origem.

estou a usar o Cromo. Eu também tentei usar o Chrome CORS Plugin Aquele plugin Chrome CORS aparentemente apenas injecta de forma simples um Access-Control-Allow-Origin': * cabeçote na resposta que o navegador vê. Se o plugin fosse mais inteligente, o que estaria a fazer seria definir o valor desse falso cabeçalho de resposta Access-Control-Allow-Origin' para a origem real do seu código frontend JavaScript,http://127.0.0.1:3000'. Portanto, evite usar esse plugin, mesmo para testes. É apenas uma distracção. Se você quiser testar quais respostas você obtém do servidor sem filtragem do navegador, é melhor usar o curl -H como acima.

No que diz respeito ao frontend do código JavaScript para a solicitação fetch(...) na pergunta:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Retire essas linhas. Os cabeçalhos Access-Control-Allow-* são cabeçalhos responsáveis. Você nunca quer enviá-los em um pedido. O único efeito que terá é acionar um navegador para fazer um pré-voo.

Comentários (8)

Este erro ocorre quando a URL do cliente e a URL do servidor don't correspondem, incluindo o número da porta. Neste caso, você precisa habilitar seu serviço para CORS que é compartilhamento de recursos de origem cruzada.

Se você está hospedando um serviço Spring REST então você pode encontrá-lo no post do blog CORS support in Spring Framework.

Se você está hospedando um serviço usando um servidor Node.js, então

  1. Pare o servidor do Node.js.

  2. Instalar o corsnpm --save'.

  3. Adicione as seguintes linhas ao seu server.js

    var cors = require('cors')

    app.use(cors()) // Use isto após a declaração da variável

Comentários (4)

Retire isto:

credentials: 'include',
Comentários (0)