Błąd XmlHttpRequest: Początek null nie jest dozwolony przez Access-Control-Allow-Origin


550

Tworzę stronę, która pobiera obrazy z Flickr i Panoramio dzięki obsłudze AJAX przez jQuery.

Strona Flickr działa dobrze, ale kiedy próbuję $.get(url, callback)z Panoramio, widzę błąd w konsoli Chrome:

XMLHttpRequest nie może załadować 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 . Pochodzenie null jest niedozwolone przez Access-Control-Allow-Origin.

Jeśli zapytam bezpośrednio ten adres URL z przeglądarki, działa dobrze. Co się dzieje i czy mogę to obejść? Czy niepoprawnie redaguję zapytanie, czy też jest to coś, co robi Panoramio, aby utrudnić to, co próbuję zrobić?

Google nie wykryło żadnych przydatnych dopasowań w komunikacie o błędzie .

EDYTOWAĆ

Oto przykładowy kod pokazujący problem:

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

Możesz uruchomić przykład online .

EDYCJA 2

Dzięki Darinowi za pomoc w tym. POWYŻSZY KOD JEST ŹLE. Zamiast tego użyj tego:

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

1
Co wygląd URL tak robisz żądania od ? To na przykład nie jest dynamicznie generowane iframe, w które document.writesię angażujesz?
Pekka

5
Czy możesz wysłać odpowiedź HTTP z żądania do każdej usługi. Założę się, że Panoramio nie obsługuje Access-Control-Allow-Origin. Zobacz w3.org/TR/cors przykłady.
Kevin Hakanson

2
@Kevin, nie potrzebujesz tych nagłówków, jeśli serwer wysyła JSONP.
Darin Dimitrov

@Pekka, w tej chwili uruchamiam stronę z mojego komputera lokalnego ( file:///C:/). Nie iframejest zaangażowany.
Drew Noakes

1
@rew, co się stanie, jeśli uruchomisz go z adresu URL http? Nie powinno to robić różnicy w ten sposób, ale po prostu wykluczyć taką możliwość.
Pekka

Odpowiedzi:


423

Dla przypomnienia, o ile wiem, miałeś dwa problemy:

  1. Nie przekazałeś specyfikatora typu „jsonp” $.get, więc używał zwykłego XMLHttpRequest. Jednak Twoja przeglądarka obsługiwała CORS (współdzielenie zasobów między źródłami), aby zezwolić na XMLHttpRequest w wielu domenach, jeśli serwer to OK. Właśnie tam Access-Control-Allow-Originwszedł nagłówek.

  2. Wydaje mi się, że wspomniałeś, że uruchomiłeś go z pliku: // URL. Nagłówki CORS mają dwa sposoby sygnalizowania, że ​​XHR między domenami jest OK. Jednym z nich jest wysłanie Access-Control-Allow-Origin: *(które, jeśli dotarłeś do Flickr za pośrednictwem $.get, musieli to robić), a drugim wysłanie echa zawartości Originnagłówka. Jednak file://adresy URL generują wartość null, Originktórej nie można autoryzować za pomocą echa zwrotnego.

Pierwszy został rozwiązany w sposób okrężny dzięki sugestii Darina $.getJSON. To trochę magiczne, aby zmienić typ żądania z domyślnego „json” na „jsonp”, jeśli widzi podciąg callback=?w adresie URL.

To rozwiązało drugie, nie próbując już wykonywać żądania CORS z file://adresu URL.

Aby wyjaśnić to innym osobom, oto proste instrukcje rozwiązywania problemów:

  1. Jeśli próbujesz użyć JSONP, upewnij się, że zachodzi jedna z następujących sytuacji:
    • Używasz $.geti ustawiasz dataTypena jsonp.
    • Używasz $.getJSONi zawarte callback=?w adresie URL.
  2. Jeśli próbujesz wykonać XMLHttpRequest między domenami za pośrednictwem CORS ...
    1. Upewnij się, że testujesz za pośrednictwem http://. Skrypty działające za pośrednictwem file://mają ograniczoną obsługę CORS.
    2. Upewnij się, że przeglądarka faktycznie obsługuje CORS . (Opera i Internet Explorer spóźniają się na imprezę)

45
Jakie jest na to rozwiązanie?
jQuerybeast,

19
oddzwonienie =? FTW
Tim

10
Niektóre przeglądarki, takie jak chrome, zezwalają na CORS, jeśli został uruchomiony z parametrem --allow-file-access-from-files
echox

1
callback=?nie działało dla mnie, ale działało jsonp=?. Jakieś wytłumaczenie tego?
zapalenie brzucha

1
Czy muszę instalować serwer WWW, aby testować http://? Czy istnieje sposób, aby po prostu otworzyć plik przy użyciu tego protokołu?
Will Sewell,

76

Być może musisz dodać HEADER do wywoływanego skryptu, oto co musiałem zrobić w PHP:

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

Więcej szczegółów w dziale AJAX ou usługi WEB (w języku francuskim).


1
@Uri: Zależy od serwera HTTP. W Apache będziesz chciał zajrzeć do mod_headers.
ssokolow

4
@Uri <meta http-equiv = "Kontrola dostępu-Allow-Origin" content = "*">
Herberth Amaral

7
@HerberthAmaral Próbowałem dodać to wewnątrz <head> </head>, ale to nie działa dla mnie. Próbuję w iOS Safari i Chrome, ale w konsoli otrzymuję wartość zerową pochodzenia nie jest dozwolona przez błąd Access-Control-Allow-Origin.
thandasoru,

3
Ze względów bezpieczeństwa nie działa w nagłówku pliku HTML. To musi być nagłówek. stackoverflow.com/questions/7015782/…
Justin Blank

Nie może być w HTML. Musi to zostać określone przez program, który wysyła stronę HTML do przeglądarki przez sieć (tak zwany serwer WWW).
Roman Plášil,

70

W przypadku prostego projektu HTML:

cd project
python -m SimpleHTTPServer 8000

Następnie przejrzyj plik.


1
podczas gdy jest niesamowity i działa, kiedy przejdziesz do 0.0.0.0:8000 i spróbujesz POSTotrzymać prośby: code 501, message Unsupported method ('POST')dla googli.
pjammer

„Gdy serwer WWW Python (na przykład cherrypy) mówi, że działa na 0.0.0.0, oznacza to, że nasłuchuje całego ruchu TCP, który kończy się na tym komputerze, bez względu na nazwę hosta lub adres IP.” Więc może spróbuj
wysłać post

20

Działa dla mnie w Google Chrome v5.0.375.127 (dostaję alert):

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

Polecam również użycie tej $.getJSON()metody, ponieważ poprzednie nie działa na IE8 (przynajmniej na moim komputerze):

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

Możesz wypróbować online stąd .


AKTUALIZACJA:

Teraz, gdy pokazałeś swój kod, widzę problem z nim. Masz zarówno funkcję anonimową, jak i wbudowaną, ale oba zostaną wywołane processImages. Tak działa obsługa JSONP przez jQuery. Zauważ, jak to definiuję callback=?, abyś mógł korzystać z anonimowej funkcji. Możesz przeczytać więcej na ten temat w dokumentacji .

Inna uwaga jest taka, że ​​nie powinieneś nazywać eval. Parametr przekazany do Twojej anonimowej funkcji zostanie już przeanalizowany w JSON przez jQuery.


Hmm ok, pozwól mi spróbować jeszcze raz. Korzystam z innej wersji Chrome btw „6.0.472.51 beta”.
Drew Noakes

Twój kod zadziałał dla mnie. Zaktualizowałem swoje pytanie za pomocą kodu, który wyjaśnia problem.
Drew Noakes

Dzięki za wskazówkę dotyczącą getJSON ... Rozwidliłem twój przykład jsFiddle, aby pokazać problem ( jsfiddle.net/ZfvKm ) i teraz widzę komunikat o błędzie XMLHttpRequest nie może załadować panoramio.com/wapi/data/… . Pochodzenie fiddle.jshell.net nie jest dozwolone przez Access-Control-Allow-Origin.
Drew Noakes

@Darin, dzięki za aktualizację. Rozumiem twój punkt widzenia i grałem z kilkoma kombinacjami tego, ale jeszcze nie znalazłem sposobu na uzyskanie dostępu do zwróconego obiektu. Czy możesz zaktualizować swój przykład jsFiddle, aby pokazać dostęp do danych? Jeśli ustawisz wywołanie zwrotne na, ?wówczas zwrócony JSON zostanie otoczony nawiasami. Nie definiujesz parametru dla funkcji zwrotnej, a wartość thisnie wydaje się mieć obiektu odpowiedzi (przynajmniej .datajest to null).
Drew Noakes,

@Darin, fantastycznie. Wielkie dzięki. Teraz działa dobrze. Dla każdego, kto to czyta, włączenie callback=?komendy jQuery generuje losową nazwę funkcji wewnętrznie, co powoduje wywołanie anonimowej funkcji, którą przekazujesz .getJSON. Doceniany.
Drew Noakes

8

Dopóki żądany serwer obsługuje format danych JSON, użyj interfejsu JSONP (JSON Padding). Pozwala tworzyć zewnętrzne żądania domeny bez serwerów proxy lub wymyślnych nagłówków.



4

Udało nam się to za pomocą http.confpliku (edytowanego, a następnie zrestartowano usługę 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>

W polu Header set Access-Control-Allow-Origin "*"możesz podać dokładny adres URL.


1
to nie działa na Apache XAMPP. problem nadal istnieje.
Raptor

1
To jest niebezpieczne. Zobacz tutaj dlaczego; stackoverflow.com/questions/7564832/…
Rob

To niebezpieczne, jak powiedział @RobQuist.
d337

4

Jeśli wykonujesz testy lokalne lub wywołujesz plik z czegoś takiego file://, musisz wyłączyć zabezpieczenia przeglądarki.

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


3

W moim przypadku ten sam kod działał poprawnie w przeglądarce Firefox, ale nie w Google Chrome. Konsola JavaScript przeglądarki Google Chrome powiedziała:

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"

Musiałem upuścić część www adresu URL Ajax, aby poprawnie pasowała do początkowego adresu URL i wtedy działało dobrze.


2

Nie wszystkie serwery obsługują jsonp. Wymaga od serwera ustawienia funkcji wywołania zwrotnego w wynikach. Używam tego, aby uzyskać odpowiedzi JSON z witryn, które zwracają czysty Json, ale nie obsługują JSON:

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

1

Używam serwera Apache, więc użyłem modułu mod_proxy. Włącz moduły:

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

Następnie dodaj:

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

Na koniec przekaż adres URL proxy do skryptu.


1

Jako ostatecznej noty dokumentacja Mozilla wyraźnie mówi, że

Powyższy przykład nie powiódłby się, gdyby nagłówek był symbolami wieloznacznymi jako: Access-Control-Allow-Origin: *. Ponieważ Access-Control-Allow-Origin wyraźnie wspomina http: //foo.example , treść rozpoznająca poświadczenie jest zwracana do wywołującej treści WWW.

W konsekwencji używanie „*” nie jest po prostu złą praktyką. Po prostu nie działa :)


1

Dla PHP - to działa dla mnie na Chrome, safari i Firefox

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

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

za pomocą axios wywołaj usługi php live z plikiem: //


To również działa ode mnie, zauważ, jak null jest STRING, a nie rzeczywistą wartością NULL. Używam go, ponieważ header('Access-Control-Allow-Origin: '.( trim($_SERVER['HTTP_REFERER'],'/') ?:'null'),true);zezwala na krzyżowanie źródła ze zdalnego serwera na inny i spada do (ciąg) null dla pliku lokalnego.
Louis Loudog Trottier

0

Otrzymałem ten sam błąd w Chrome (nie testowałem innych przeglądarek). Stało się tak dlatego, że nawigowałem na domain.com zamiast www.domain.com. Trochę dziwne, ale mogłem rozwiązać problem, dodając następujące wiersze do .htaccess. Przekierowuje domain.com do www.domain.com i problem został rozwiązany. Jestem leniwym gościem sieciowym, więc prawie nigdy nie wpisuję www, ale najwyraźniej w niektórych przypadkach jest to wymagane.

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


0

Ludzie,

Wystąpił podobny problem. Ale używając Fiddlera udało mi się rozwiązać problem. Problem polega na tym, że adres URL klienta skonfigurowany w implementacji CORS po stronie interfejsu API sieci Web nie może zawierać końcowego ukośnika. Po przesłaniu żądania przez Google Chrome i sprawdzeniu karty TextView w sekcji Nagłówki Fiddlera komunikat o błędzie brzmi mniej więcej tak:

* „Określone źródło zasad twoja_klient_url: /” jest nieprawidłowe. Nie może kończyć się ukośnikiem. ”

To naprawdę dziwne, ponieważ działało bez żadnych problemów w Internet Explorerze, ale sprawiło mi ból głowy podczas testowania za pomocą Google Chrome.

Usunąłem ukośnik w kodzie CORS i ponownie skompilowałem interfejs API sieci Web, a teraz interfejs API jest dostępny bez żadnych problemów za pośrednictwem Chrome i Internet Explorera. Spróbuj tego.

Dzięki, Andy


0

W powyższym rozwiązaniu opublikowanym przez CodeGroover występuje niewielki problem. Jeśli zmienisz plik, będziesz musiał zrestartować serwer, aby faktycznie użyć zaktualizowanego pliku (przynajmniej w moim przypadku).

Więc szukając trochę, znalazłem ten Aby użyć:

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

A potem będzie służyć http://localhost:8000.


1
Chociaż ten link może odpowiedzieć na pytanie, lepiej dołączyć tutaj istotne części odpowiedzi i podać link w celach informacyjnych. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie. - Z recenzji
Vi100

Wyjaśniono link.
MiJyn
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.