Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado - ao tentar obter dados de uma API REST

469

Estou tentando buscar alguns dados da API REST do HP Alm. Funciona muito bem com um pequeno script curl - eu recebo meus dados.

Agora, fazer isso com JavaScript, fetch e ES6 (mais ou menos) parece ser um problema maior. Continuo recebendo esta mensagem de erro:

A API de busca não pode carregar. A resposta à solicitação de comprovação não passa na verificação do controle de acesso: Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Portanto, a origem ' http://127.0.0.1:3000 ' não é permitida. A resposta tinha o código de status HTTP 501. Se uma resposta opaca atender às suas necessidades, configure o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Entendo que isso ocorre porque estou tentando buscar esses dados no meu host local e a solução deve estar usando o CORS. Agora eu pensei que realmente fiz isso, mas de alguma forma isso ignora o que escrevo no cabeçalho ou o problema é outra coisa?

Então, há um problema de implementação? Estou fazendo errado? Infelizmente, não consigo verificar os logs do servidor. Estou realmente um pouco preso 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 usando o Chrome. Também tentei usar esse plug-in do Chrome CORS, mas estou recebendo outra mensagem de erro:

O valor do cabeçalho 'Access-Control-Allow-Origin' na resposta não deve ser o curinga '*' quando o modo de credenciais da solicitação for 'include'. Portanto, a origem ' http://127.0.0.1:3000 ' não é permitida. O modo de credenciais de solicitações iniciadas pelo XMLHttpRequest é controlado pelo atributo withCredentials.

daniel.lozynski
fonte

Respostas:

825

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

  • Como usar um proxy CORS dar a volta “No Access-Control-Allow-Origin cabeçalho” problemas
  • Como evitar o preflight do CORS
  • Como corrigir os problemas do cabeçalho "Acesso-controle-permissão-origem" não deve ser o curinga

Como usar um proxy CORS dar a volta “No Access-Control-Allow-Origin cabeçalho” problemas

Se você não controlar o servidor, seu código JavaScript de front-end está enviando uma solicitação e o problema com a resposta desse servidor é apenas a falta do Access-Control-Allow-Origincabeçalho necessário , você ainda poderá fazer as coisas funcionarem - fazendo a solicitação por meio de um Proxy CORS. Para mostrar como isso funciona, primeiro, aqui está um código que não usa 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 catchbloco é atingido é que o navegador impede que esse código acesse a resposta da qual ele volta https://example.com. E a razão pela qual o navegador faz isso é que a resposta não possui o Access-Control-Allow-Origincabeçalho de resposta.

Agora, aqui está exatamente o mesmo exemplo, mas apenas com um proxy CORS adicionado em:

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?"))

Nota: Se https://cors-anywhere.herokuapp.com estiver inativo ou indisponível quando você o experimentar, veja abaixo como implantar 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 êxito, porque pegar o URL da solicitação e alterá-lo para https://cors-anywhere.herokuapp.com/https://example.com - apenas prefixando-o com o URL do proxy - causa o solicitação a ser feita através desse proxy, que então:

  1. Encaminha a solicitação para https://example.com.
  2. Recebe a resposta de https://example.com.
  3. Adiciona o Access-Control-Allow-Origincabeçalho à resposta.
  4. Passa essa resposta, com esse cabeçalho adicionado, de volta ao código de front-end solicitante.

O navegador então permite que o código de front-end acesse a resposta, porque essa resposta com o Access-Control-Allow-Origincabeçalho da resposta é o que o navegador vê.

Você pode executar facilmente seu próprio proxy usando o código de https://github.com/Rob--W/cors-anywhere/ .
Você também pode implantar facilmente seu próprio proxy no 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

Após a execução desses comandos, você terminará com seu próprio servidor CORS Anywhere em execução, por exemplo, em https://cryptic-headland-94862.herokuapp.com/ . Portanto, em vez de prefixar seu URL de solicitação https://cors-anywhere.herokuapp.com, prefixe-o com o URL da sua própria instância; por exemplo, https://cryptic-headland-94862.herokuapp.com/https://example.com .

Portanto, se você tentar usar https://cors-anywhere.herokuapp.com, descobrir que está em baixo (o que às vezes acontece), considere obter uma conta Heroku (se ainda não o fez) e faça 2 ou 3 minutos para executar as etapas acima para implantar seu próprio servidor CORS Anywhere no Heroku.

Independentemente de você executar ou usar https://cors-anywhere.herokuapp.com ou outro proxy aberto, esta solução funcionará mesmo se a solicitação for aquela que aciona os navegadores para fazer uma OPTIONSsolicitação de comprovação CORS - porque, nesse caso, o O proxy também envia de volta os cabeçalhos Access-Control-Allow-Headerse Access-Control-Allow-Methodsnecessários para tornar o preflight bem-sucedido.


Como evitar o preflight do CORS

O código na pergunta aciona um preflight do CORS, pois envia um Authorizationcabeçalho.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

Mesmo sem isso, o Content-Type: application/jsoncabeçalho também acionaria o preflight.

O que “comprovar” significa: antes que o navegador tente digitar o POSTcódigo da pergunta, ele primeiro envia uma OPTIONSsolicitação ao servidor - para determinar se o servidor está optando por receber uma origem cruzada POSTque inclua os cabeçalhos Authorizatione Content-Type: application/json.

Funciona muito bem com um pequeno script curl - eu recebo meus dados.

Para testar corretamente curl, você deve emular a OPTIONSsolicitação de comprovação 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"

... https://the.sign_in.urlsubstituído por qualquer que seja o seu sign_inURL real .

A resposta que o navegador precisa receber dessa OPTIONSsolicitação 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 OPTIONSresposta não incluir esses cabeçalhos, o navegador será interrompido e nunca tentará enviar a POSTsolicitação. Além disso, o código de status HTTP para a resposta deve ser 2xx - normalmente 200 ou 204. Se houver outro código de status, o navegador irá parar ali.

O servidor na pergunta está respondendo à OPTIONSsolicitação com um código de status 501, o que aparentemente significa que está tentando indicar que não implementa suporte para OPTIONSsolicitações. Outros servidores normalmente respondem com um código de status 405 "Método não permitido" neste caso.

Portanto, você nunca poderá fazer POSTsolicitações diretamente a esse servidor a partir do seu código JavaScript de front-end se o servidor responder a essa OPTIONSsolicitação com um 405 ou 501 ou qualquer coisa que não seja um 200 ou 204 ou se não responder com os necessários cabeçalhos de resposta.

A maneira de evitar o desencadeamento de um preflight para o caso na pergunta seria:

  • se o servidor não exigir um Authorizationcabeçalho de solicitação, mas (por exemplo) se basear nos dados de autenticação incorporados no corpo da POSTsolicitação ou como um parâmetro de consulta
  • se o servidor não exigiu que o POSTcorpo tivesse um Content-Type: application/jsontipo de mídia, mas o aceitou POSTcomo application/x-www-form-urlencodedcom um parâmetro chamado json(ou qualquer outro) cujo valor são os dados JSON

Como corrigir os problemas do cabeçalho "Acesso-controle-permissão-origem" não deve ser o curinga

Estou recebendo outra mensagem de erro:

O valor do cabeçalho 'Access-Control-Allow-Origin' na resposta não deve ser o curinga '*' quando o modo de credenciais da solicitação for 'include'. Portanto, a origem ' http://127.0.0.1:3000 ' não é permitida. O modo de credenciais de solicitações iniciadas pelo XMLHttpRequest é controlado pelo atributo withCredentials.

Para uma solicitação que inclui credenciais, os navegadores não permitirão que o código JavaScript do front-end acesse a resposta se o valor do Access-Control-Allow-Origincabeçalho da resposta for *. Em vez disso, o valor nesse caso deve corresponder exatamente à origem do seu código de front-end http://127.0.0.1:3000,.

Consulte Solicitações credenciadas e curingas no artigo MDN HTTP access control (CORS).

Se você controla o servidor para o qual está enviando a solicitação, uma maneira comum de lidar com esse caso é configurar o servidor para pegar o valor do Origincabeçalho da solicitação e repetir / refletir isso no valor do Access-Control-Allow-Origincabeçalho da resposta. Por exemplo, com nginx:

add_header Access-Control-Allow-Origin $http_origin

Mas esse é apenas um exemplo; outros sistemas de servidor (web) fornecem maneiras semelhantes de ecoar valores de origem.


Estou usando o Chrome. Eu também tentei usar o Chrome CORS Plugin

Esse plug-in do Chrome CORS aparentemente injeta simplesmente um Access-Control-Allow-Origin: *cabeçalho na resposta que o navegador vê. Se o plug-in fosse mais inteligente, o que estaria fazendo seria definir o valor desse Access-Control-Allow-Origincabeçalho de resposta falsa para a origem real do seu código JavaScript de front-end http://127.0.0.1:3000,.

Portanto, evite usar esse plug-in, mesmo para testes. É apenas uma distração. Se você deseja testar quais respostas você obtém do servidor sem que o navegador as filtre, é melhor usá-lo curl -Hcomo acima.


Quanto ao código JavaScript de front-end para a fetch(…)solicitação na pergunta:

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

Remova essas linhas. Os Access-Control-Allow-*cabeçalhos são cabeçalhos de resposta . Você nunca deseja enviá-los em uma solicitação. O único efeito que isso terá é acionar um navegador para realizar um preflight.

sideshowbarker
fonte
1
Portanto, o URL se pareceria com: 127.0.0.1:9999/127.0.0.1:5000 com cors, certo?
Daniel.lozynski
Se você está executando sua própria instância do código github.com/Rob--W/cors-anywhere em 127.0.0.1:9999 e deseja fazer uma solicitação para 127.0.0.1:5000 , sim
sideshowbarker
4
Resposta soberba: meu problema foi o servidor remoto não responder às solicitações OPTIONS, então, depois de mexer com solicitações e cabeçalhos pelo que pareciam anos, resolvi removendo os cabeçalhos Content-Typee Access-Control-Allow-Origin- obrigado!
Morvael 29/01/19
1
Não gosto da ideia de usar um proxy, a menos que seja um que você controle, caso contrário, você estará buscando os dados por meio de um terceiro não confiável e aberto a adulterações etc.
DaveMongoose
1
Essa é uma resposta profunda e excelente, obrigado. O que eu pessoalmente fiz ao testar foi o Open chrome com sinalizador de segurança desativar a web e funcionou. open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Karun
101

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

Se você estiver hospedando um serviço Spring REST, poderá encontrá-lo no suporte ao CORS da postagem do blog no Spring Framework .

Se você estiver hospedando um serviço usando um servidor Node.js.,

  1. Pare o servidor Node.js.
  2. npm install cors --save
  3. Adicione as seguintes linhas ao seu server.js

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration
Rakesh
fonte
4
Trabalhou sem quaisquer problemas ... muito útil com recurso link primavera .. Muito obrigado
Rajesh Mbm
4
including the port number:(
scottysseus
1
Realmente útil vc ter feito o meu dia
kunal pal
número da porta salvou minha vida
Ashad
Resposta mais simples de sempre, obrigado :)
stacks
47

O problema surgiu porque você adicionou o seguinte código como cabeçalho de solicitação no seu front-end:

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

Esses cabeçalhos pertencem à resposta , não à solicitação. Portanto, remova- os, incluindo a linha:

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

Sua solicitação tinha 'Content-Type: application / json', portanto, acionou o que é chamado de comprovação CORS. Isso fez com que o navegador enviasse a solicitação com o método OPTIONS. Consulte CORS preflight para obter informações detalhadas.
Portanto, em seu back-end , você deve lidar com essa solicitação pré-comprovada retornando os cabeçalhos de resposta que incluem:

Access-Control-Allow-Origin : http://localhost:3000
Access-Control-Allow-Credentials : true
Access-Control-Allow-Methods : GET, POST, OPTIONS
Access-Control-Allow-Headers : Origin, Content-Type, Accept

Obviamente, a sintaxe real depende da linguagem de programação usada para seu back-end.

No seu front-end, deve ser assim:

function performSignIn() {
    let headers = new Headers();

    headers.append('Content-Type', 'application/json');
    headers.append('Accept', 'application/json');
    headers.append('Authorization', 'Basic ' + base64.encode(username + ":" +  password));
    headers.append('Origin','http://localhost:3000');

    fetch(sign_in, {
        mode: 'cors',
        credentials: 'include',
        method: 'POST',
        headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}
Lex Soft
fonte
4
Essa deve ser a resposta principal - eu realmente não gosto da idéia de ignorar o CORS, principalmente encaminhando-o por terceiros.
precisa saber é o seguinte
ei, o que é 'Header ()' por favor?
Mitsu
@mitsu Se você se referir à linha: deixe headers = new Headers (); acima, é uma interface da API de busca para executar ações com cabeçalhos de solicitação ou resposta http. Visite developer.mozilla.org/pt-BR/docs/Web/API/Headers para obter detalhes e exemplos de como usá-lo.
Lex Soft
1
Resposta clara e precisa. É lógico e aborda exatamente o problema. - Thx
Dhammika 14/02
7

No meu caso, eu uso a solução abaixo

Front-end ou Angular

post(
    this.serverUrl, dataObjToPost,
    {
      headers: new HttpHeaders({
           'Content-Type':  'application/json',
         })
    }
)

back-end (eu uso php)

header("Access-Control-Allow-Origin: http://localhost:4200");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header("Access-Control-Allow-Headers: Content-Type, Authorization");

$postdata = file_get_contents("php://input");
$request = json_decode($postdata);
print_r($request);
Harrison O
fonte
2

Apenas meus dois centavos ... sobre como usar um proxy CORS para contornar "NãoAccess-Control-Allow-Origin cabeçalho"

Para aqueles que trabalham com php no back-end, implantar um "proxy CORS" é tão simples quanto:

  1. crie um arquivo chamado 'no-cors.php' com o seguinte conteúdo:

    $URL = $_GET['url']; echo json_encode(file_get_contents($URL)); die();

  2. no seu front end, faça algo como:

    fetch('https://example.com/no-cors.php' + '?url=' + url) .then(response=>{*/Handle Response/*})

Yair Levy
fonte
1

Tire isto:

credentials: 'include',
Nalan Madheswaran
fonte
1

Usando dataType: 'jsonp'funcionou para mim.

   async function get_ajax_data(){
       var _reprojected_lat_lng = await $.ajax({
                                type: 'GET',
                                dataType: 'jsonp',
                                data: {},
                                url: _reprojection_url,
                                error: function (jqXHR, textStatus, errorThrown) {
                                    console.log(jqXHR)
                                },
                                success: function (data) {
                                    console.log(data);

                                    // note: data is already json type, you
                                    //       just specify dataType: jsonp
                                    return data;
                                }
                            });


 } // function               
hoogw
fonte
1

No meu caso, o servidor da web impediu o método "OPTIONS"

Verifique seu servidor da web para o método de opções

Estou usando "webtier"

/www/webtier/domains/[namedomainname.la/config/fmwconfig/components/OHS/VCWeb1/httpd.conf

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

mudar para

<IfModule mod_rewrite.c>
  RewriteEngine off
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>
Juneho Nam
fonte
0

Eu estava trabalhando com o Spring REST e resolvi adicionando os AllowedMethods ao WebMvcConfigurer.

@Value( "${app.allow.origins}" )
    private String allowOrigins;    
@Bean
public WebMvcConfigurer corsConfigurer() {
            System.out.println("allow origin: "+allowOrigins);
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**")
                    //.allowedOrigins("http://localhost")
                    .allowedOrigins(allowOrigins)
                    .allowedMethods("PUT", "DELETE","GET", "POST");
                }
            };
        }
Jorge Dominguez
fonte
-1

Eu estava recebendo esse erro no meu local. Executei o Visual Studio como administrador e ele foi resolvido.

ugurrrrr
fonte