Erro XmlHttpRequest: a origem nula não é permitida pelo Access-Control-Allow-Origin

550

Estou desenvolvendo uma página que extrai imagens do Flickr e do Panoramio por meio do suporte AJAX do jQuery.

O lado do Flickr está funcionando bem, mas quando tento no $.get(url, callback)Panoramio, vejo um erro no console do Chrome:

O XMLHttpRequest não pode carregar http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&callback=processImages&minx=-30&miny=0&maxx=0&maxy=150 . A origem nula não é permitida pelo Access-Control-Allow-Origin.

Se eu consultar esse URL diretamente de um navegador, ele funcionará bem. O que está acontecendo e posso contornar isso? Estou compondo minha consulta incorretamente ou isso é algo que o Panoramio faz para impedir o que estou tentando fazer?

O Google não encontrou nenhuma correspondência útil na mensagem de erro .

EDITAR

Aqui está um código de exemplo que mostra o problema:

$().ready(function () {
  var url = 'http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&callback=processImages&minx=-30&miny=0&maxx=0&maxy=150';

  $.get(url, function (jsonp) {
    var processImages = function (data) {
      alert('ok');
    };

    eval(jsonp);
  });
});

Você pode executar o exemplo online .

EDIT 2

Agradeço a Darin por sua ajuda nisso. O CÓDIGO ACIMA ESTÁ ERRADO. Use isto:

$().ready(function () {
  var url = 'http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&minx=-30&miny=0&maxx=0&maxy=150&callback=?';

  $.get(url, function (data) {
    // can use 'data' in here...
  });
});
Drew Noakes
fonte
1
Como é o URL como o que você está fazendo o pedido de ? Isso não é gerado dinamicamente para iframevocê document.write, por exemplo?
Pekka
5
Você pode postar a resposta HTTP da solicitação em cada serviço. Aposto que o Panoramio não está fornecendo acesso-controle-permissão-origem. Veja w3.org/TR/cors para exemplos.
precisa saber é o seguinte
2
@ Kevin, você não precisa desses cabeçalhos se o servidor enviar JSONP.
Darin Dimitrov
@Pekka, estou executando a página da minha máquina local no momento ( file:///C:/). Não iframeestá envolvido.
Drew Noakes
1
@ desenhou o que acontece se você executá-lo a partir de um URL http? Não deve fazer a diferença desta maneira, mas apenas excluir a possibilidade.
Pekka

Respostas:

423

Para que conste, até onde sei, você teve dois problemas:

  1. Você não estava passando um especificador de tipo "jsonp" para o seu $.get, portanto estava usando um XMLHttpRequest comum. No entanto, seu navegador oferece suporte ao CORS (compartilhamento de recursos de origem cruzada) para permitir XMLHttpRequest entre domínios, se o servidor o aceitar. Foi aí que o Access-Control-Allow-Origincabeçalho entrou.

  2. Acredito que você tenha mencionado que estava executando a partir de um arquivo: // URL. Existem duas maneiras de os cabeçalhos do CORS sinalizarem que um XHR entre domínios está OK. Uma é enviar Access-Control-Allow-Origin: *(o que, se você estava acessando o Flickr via $.get, eles deviam estar fazendo) enquanto a outra era ecoar o conteúdo do Origincabeçalho. No entanto, os file://URLs produzem um nulo Originque não pode ser autorizado por meio de eco-back.

O primeiro foi resolvido de forma indireta pela sugestão de Darin $.getJSON. É um pouco mágico mudar o tipo de solicitação do padrão "json" para "jsonp" se ele vir a substring callback=?na URL.

Isso resolveu o segundo ao não tentar mais executar uma solicitação CORS a partir de uma file://URL.

Para esclarecer para outras pessoas, aqui estão as instruções simples para solução de problemas:

  1. Se você estiver tentando usar JSONP, verifique se um dos seguintes é o caso:
    • Você está usando $.gete definido dataTypecomo jsonp.
    • Você está usando $.getJSONe incluído callback=?no URL.
  2. Se você estiver tentando fazer um XMLHttpRequest entre domínios via CORS ...
    1. Verifique se você está testando via http://. Os scripts em execução via file://têm suporte limitado ao CORS.
    2. Verifique se o navegador realmente suporta o CORS . (Opera e Internet Explorer estão atrasados ​​para a festa)
ssokolow
fonte
45
Então, qual é a solução para isso?
JQuerybeast
19
retorno de chamada =? FTW
Tim
10
Alguns navegadores como o Chrome permitem CORS se iniciado com arquivos-file-access-from---allow o parâmetro
echox
1
callback=?não funcionou para mim, mas jsonp=?funcionou. Alguma explicação para isso?
crunkchitis
1
Preciso instalar um servidor web para testar via http://? Ou existe uma maneira de simplesmente abrir o arquivo usando esse protocolo?
Will Sewell
76

Talvez você precise adicionar um HEADER no seu script chamado, aqui está o que eu tive que fazer no PHP:

header('Access-Control-Allow-Origin: *');

Mais detalhes em Vários domínios AJAX ou serviços WEB (em francês).

Thomas Decaux
fonte
1
@Uri: Depende do seu servidor HTTP. Com o Apache, você deve procurar por mod_headers.
S03
4
@Uri <meta http-equiv = "Acesso-controle-permitir-origem" content = "*">
Herberth Amaral
7
@HerberthAmaral Tentei adicionar isso dentro de <head> </head>, mas não funciona para mim. Estou tentando no iOS Safari e Chrome, mas no console, recebo a origem nula não é permitida pelo erro Access-Control-Allow-Origin.
Thandasoru 02/04/12
3
Ele não funciona no cabeçalho do arquivo html por razões de segurança. Tem que ser um cabeçalho. stackoverflow.com/questions/7015782/…
Justin Blank
Não pode estar no HTML. Ele deve ser especificado pelo programa que está enviando a página HTML para o seu navegador pela rede (o chamado servidor da web).
Roman Plášil
70

Para um projeto HTML simples:

cd project
python -m SimpleHTTPServer 8000

Em seguida, procure seu arquivo.

CodeGroover
fonte
1
Enquanto impressionante e funciona, quando você vai para 0.0.0.0:8000 e tenta POSTsolicitações que você recebe: code 501, message Unsupported method ('POST')para os googles.
pjammer
"Quando um servidor Web Python (como cherrypy, por exemplo) diz que está servindo no 0.0.0.0, significa que está ouvindo todo o tráfego TCP que termina naquela máquina, independentemente do nome do host ou IP solicitado." Então talvez tente postar no localhost: 8000 stackoverflow.com/a/4341808/102022
eric.christensen
20

Funciona para mim no Google Chrome v5.0.375.127 (recebo o alerta):

$.get('http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&callback=?&minx=-30&miny=0&maxx=0&maxy=150',
function(json) {
    alert(json.photos[1].photoUrl);
});

Também recomendo que você use o $.getJSON()método, pois o anterior não funciona no IE8 (pelo menos na minha máquina):

$.getJSON('http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&callback=?&minx=-30&miny=0&maxx=0&maxy=150', 
function(json) {
    alert(json.photos[1].photoUrl);
});

Você pode experimentá-lo online a partir daqui .


ATUALIZAR:

Agora que você mostrou seu código, posso ver o problema. Você está tendo uma função anônima e uma função embutida, mas ambas serão chamadas processImages. É assim que o suporte a JSONP do jQuery funciona. Observe como estou definindo o callback=?para que você possa usar uma função anônima. Você pode ler mais sobre isso na documentação .

Outra observação é que você não deve chamar eval. O parâmetro passado para sua função anônima já será analisado em JSON pelo jQuery.

Darin Dimitrov
fonte
Hmm ok, deixe-me tentar novamente. Estou em uma versão diferente do Chrome btw "6.0.472.51 beta".
de Drew Noakes
Seu código funcionou para mim. Atualizei minha pergunta com algum código que desenha o problema.
de Drew Noakes
Obrigado pela dica re getJSON ... Bifurquei seu exemplo jsFiddle para mostrar o problema ( jsfiddle.net/ZfvKm ) e agora veja a mensagem de erro XMLHttpRequest não pode carregar panoramio.com/wapi/data/… . A origem fiddle.jshell.net não é permitida pelo Access-Control-Allow-Origin.
de Drew Noakes
@ Darin, obrigado pela atualização. Entendo seu argumento e já brinquei com algumas combinações disso, mas ainda não encontrei uma maneira de acessar o objeto retornado. Você poderia atualizar seu exemplo do jsFiddle para mostrar o acesso aos dados? Se você definir o retorno de chamada como ?, o JSON retornado será colocado entre parênteses. Você não define um parâmetro para sua função de retorno de chamada e o valor de thisnão parece ter um objeto de resposta (pelo menos, o .datavalor é null).
Drew Noakes
@ Darin, fantástico. Muito Obrigado. Trabalhando bem agora. Para qualquer pessoa que esteja lendo isso, a inclusão de callback=?indica ao jQuery para gerar internamente um nome de função aleatória, resultando em uma chamada para a função anônima que você passa .getJSON. Estimado.
Drew Noakes
8

Desde que o servidor solicitado suporte o formato de dados JSON, use a interface JSONP (JSON Padding). Ele permite que você faça solicitações de domínio externo sem servidores proxy ou itens de cabeçalho sofisticados.

Cheng Chen
fonte
5

É a mesma política de origem , é necessário usar uma interface JSON-P ou um proxy em execução no mesmo host.

Quentin
fonte
4

Nós o gerenciamos através do http.confarquivo (editado e, em seguida, reiniciado o serviço HTTP):

<Directory "/home/the directory_where_your_serverside_pages_is">
    Header set Access-Control-Allow-Origin "*"
    AllowOverride all
    Order allow,deny
    Allow from all
</Directory>

No Header set Access-Control-Allow-Origin "*", você pode colocar um URL preciso.

romu31
fonte
1
isso não funciona no Apache do XAMPP. o problema ainda existe.
Raptor
1
Isso não é seguro. Veja aqui o porquê; stackoverflow.com/questions/7564832/…
Rob
É inseguro como o @RobQuist disse.
d337 3/01/19
4

Se você estiver testando localmente ou chamando o arquivo de algo parecido file://, precisará desativar a segurança do navegador.

No MAC: open -a Google\ Chrome --args --disable-web-security

user2701060
fonte
3

No meu caso, o mesmo código funcionou bem no Firefox, mas não no Google Chrome. O console JavaScript do Google Chrome disse:

XMLHttpRequest cannot load http://www.xyz.com/getZipInfo.php?zip=11234. 
Origin http://xyz.com is not allowed by Access-Control-Allow-Origin.
Refused to get unsafe header "X-JSON"

Eu tive que soltar a parte www do URL do Ajax para que ele correspondesse corretamente ao URL de origem e funcionou bem então.

Kalpesh Patel
fonte
2

Nem todos os servidores suportam jsonp. Exige que o servidor defina a função de retorno de chamada em seus resultados. Eu uso isso para obter respostas json de sites que retornam json puro, mas não suportam jsonp:

function AjaxFeed(){

    return $.ajax({
        url:            'http://somesite.com/somejsonfile.php',
        data:           {something: true},
        dataType:       'jsonp',

        /* Very important */
        contentType:    'application/json',
    });
}

function GetData() {
    AjaxFeed()

    /* Everything worked okay. Hooray */
    .done(function(data){
        return data;
    })

    /* Okay jQuery is stupid manually fix things */
    .fail(function(jqXHR) {

        /* Build HTML and update */
        var data = jQuery.parseJSON(jqXHR.responseText);

        return data;
    });
}
mAsT3RpEE
fonte
1

Eu uso o servidor Apache, então usei o módulo mod_proxy. Ativar módulos:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

Adicione então:

ProxyPass /your-proxy-url/ http://service-url:serviceport/

Por fim, passe o proxy-url para o seu script.

zenio
fonte
1

Como nota final, a documentação do Mozilla diz explicitamente que

O exemplo acima falharia se o cabeçalho fosse curinga como: Access-Control-Allow-Origin: *. Como o Access-Control-Allow-Origin menciona explicitamente http: //foo.example , o conteúdo com reconhecimento de credencial é retornado ao conteúdo da Web que está chamando.

Como conseqüência, não é simplesmente uma prática ruim usar '*'. Simplesmente não funciona :)

user2688838
fonte
1

Para PHP - este trabalho para mim no Chrome, safari e firefox

https://w3c.github.io/webappsec-cors-for-developers/#avoid-returning-access-control-allow-origin-null

header('Access-Control-Allow-Origin: null');

usando axios chame php live services com file: //

Tyler
fonte
Isso também funciona de mim, observe como null é um STRING e não é um NULL real. Eu o uso assim como header('Access-Control-Allow-Origin: '.( trim($_SERVER['HTTP_REFERER'],'/') ?:'null'),true);permite a origem cruzada de um servidor remoto para outro e cai para (string) null para o arquivo local.
Louis Loudog Trottier
0

Também recebi o mesmo erro no Chrome (não testei outros navegadores). Isso ocorreu porque eu estava navegando no domain.com em vez de www.domain.com. Um pouco estranho, mas eu poderia resolver o problema adicionando as seguintes linhas ao .htaccess. Ele redireciona domain.com para www.domain.com e o problema foi resolvido. Como sou um visitante preguiçoso, quase nunca digito o www, mas aparentemente em alguns casos é necessário.

RewriteEngine on
RewriteCond %{HTTP_HOST} ^domain\.com$ [NC]
RewriteRule ^(.*)$ http://www.domain.com/$1 [R=301,L]
mslembro
fonte
0

Verifique se você está usando a versão mais recente do JQuery. Estávamos enfrentando esse erro no JQuery 1.10.2 e o erro foi resolvido após o uso do JQuery 1.11.1

Ganesh Kamath - 'Code Frenzy'
fonte
0

Pessoal,

Eu tive um problema semelhante. Mas, usando o Fiddler, consegui entender o problema. O problema é que a URL do cliente configurada na implementação do CORS no lado da API da Web não deve ter uma barra final à direita. Depois de enviar sua solicitação pelo Google Chrome e inspecionar a guia TextView da seção Headers do Fiddler, a mensagem de erro informa algo como isto:

* "A origem da política especificada your_client_url: / 'é inválida. Não pode terminar com uma barra."

Isso é muito peculiar, porque funcionou sem problemas no Internet Explorer, mas me deu uma dor de cabeça ao testar usando o Google Chrome.

Eu removi a barra no código CORS e recompilei a API da Web, e agora a API está acessível via Chrome e Internet Explorer sem problemas. Por favor, dê uma chance.

Obrigado Andy

andymenon
fonte
0

Há um pequeno problema na solução postada pelo CodeGroover acima , onde se você alterar um arquivo, terá que reiniciar o servidor para realmente usar o arquivo atualizado (pelo menos no meu caso).

Então, pesquisando um pouco, encontrei este Para usar:

sudo npm -g install simple-http-server # to install
nserver # to use

E então ele servirá às http://localhost:8000.

MiJyn
fonte
1
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada. - Do comentário
Vi100
Esclarecido o link.
MiJyn 5/05