jQuery xml-Fehler 'In der angeforderten Ressource ist kein Header' Access-Control-Allow-Origin 'vorhanden.'

89

Ich arbeite an meinem persönlichen Projekt nur zum Spaß, wo ich eine XML-Datei lesen möchte, die sich unter http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml befindet, und die XML- und XML-Datei analysieren möchte Verwenden Sie diese Option, um Werte zwischen den Währungen umzurechnen.

Bisher habe ich mir den folgenden Code ausgedacht, der ziemlich einfach ist, um die XML zu lesen, aber ich erhalte den folgenden Fehler.

XMLHttpRequest kann **** nicht laden. In der angeforderten Ressource ist kein Header 'Access-Control-Allow-Origin' vorhanden. Origin ' http://run.jsbin.com ' ist daher kein Zugriff gestattet.

$(document).ready( 
    function() {     
        $.ajax({          
            type:  'GET',
            url:   'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',
            dataType: 'xml',              
            success: function(xml){
                alert('aaa');
            }
         });
    }
);

Ich sehe nichts falsches an meinem Code, also hoffe ich, dass jemand darauf hinweisen kann, was ich mit meinem Code falsch mache und wie ich es beheben kann.

Bazinga777
quelle
2
Ich schlage vor, Sie lesen die Same Origin Policy und CORS
jmoerdyk
Der Fehler gibt Wort für Wort genau an, was falsch ist. Ihr Code ist in Ordnung, das Problem liegt bei dem Server, auf den Sie zugreifen.
Kevin B
und siehe auch CORS auf MDN
Amir Ali Akbari

Antworten:

163

Sie können aufgrund der Richtlinie mit demselben Ursprung keinen Ajax-Aufruf http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xmlvon einer Datei aus tätigen, die unter bereitgestellt wurde .http://run.jsbin.com


Da sich die Quellseite (auch als Ursprungsseite bezeichnet ) und die Ziel- URL in verschiedenen Domänen ( run.jsbin.comund www.ecb.europa.eu) befinden, versucht Ihr Code tatsächlich, eine domänenübergreifende (CORS) Anforderung zu stellen, die keine gewöhnliche ist GET.

Mit wenigen Worten, die Richtlinie mit demselben Ursprung besagt, dass Browser nur Ajax-Aufrufe an Dienste in derselben Domäne der HTML-Seite zulassen sollten .


Beispiel:

Eine Seite bei http://www.example.com/myPage.htmlnur direkt anfordern Dienste , die auf sind http://www.example.com, wie http://www.example.com/api/myService. Wenn der Dienst in einer anderen Domain gehostet wird (z. B. http://www.ok.com/api/myService), führt der Browser den Anruf nicht direkt durch (wie erwartet). Stattdessen wird versucht, eine CORS-Anfrage zu stellen.

Um es kurz zu machen, um eine (CORS) Anfrage * über verschiedene Domänen hinweg auszuführen, Ihr Browser:

  • OriginFügt der ursprünglichen Anforderung einen Header hinzu (mit der Domain der Seite als Wert) und führt ihn wie gewohnt aus. und dann
  • Nur wenn die Server - Antwort auf diese Anforderung die enthält ausreichende Header ( Access-Control-Allow-Originist einer von ihnen ) so dass der CORS anfordern, wird vervollständigt das Durchsuchen , um den Anruf (fast ** genau so , wie es wäre , wenn die HTML - Seite in der gleichen Domäne ist).
    • Wenn die erwarteten Header nicht kommen, gibt der Browser einfach auf (wie bei Ihnen).


* Das Obige zeigt die Schritte in einer einfachen Anfrage, z. B. eine reguläre Anfrage GETohne ausgefallene Header. Wenn die Anfrage nicht einfach ist (wie eine POSTmit application/jsonals Inhaltstyp), hält der Browser sie einen Moment lang an und sendet vor der Erfüllung zunächst eine OPTIONSAnfrage an die Ziel-URL. Wie oben wird es nur fortgesetzt, wenn die Antwort auf diese OPTIONSAnforderung die CORS-Header enthält. Dieser OPTIONSAufruf wird als Preflight- Anforderung bezeichnet.
** Ich sage fast, weil es andere Unterschiede zwischen regulären Anrufen und CORS-Anrufen gibt. Ein wichtiger Punkt ist, dass einige Header, auch wenn sie in der Antwort vorhanden sind, vom Browser nicht erfasst werden, wenn sie nicht imAccess-Control-Expose-Headers Header enthalten sind.


Wie man es repariert?

War es nur ein Tippfehler? Manchmal hat der JavaScript-Code nur einen Tippfehler in der Zieldomäne. Hast du kontrolliert? Wenn die Seite auf ist www.example.com, werden nur regelmäßige Anrufe getätigt www.example.com! Andere URLs, wie api.example.comoder sogar example.comoder www.example.com:8080werden vom Browser als andere Domains betrachtet ! Ja, wenn der Port anders ist, dann ist es eine andere Domain!

Fügen Sie die Überschriften hinzu. Die einfachste Möglichkeit, CORS zu aktivieren, besteht darin Access-Control-Allow-Origin, den Antworten des Servers die erforderlichen Header (as ) hinzuzufügen . (Jeder Server / jede Sprache hat eine Möglichkeit, dies zu tun - überprüfen Sie hier einige Lösungen .)

Letzter Ausweg: Wenn Sie keinen serverseitigen Zugriff auf den Dienst haben, können Sie ihn auch spiegeln (über Tools wie Reverse-Proxys ) und dort alle erforderlichen Header einfügen.

acdcjunior
quelle
2
Danke, das sind viele Informationen. Jetzt kann ich die notwendigen Nachforschungen anstellen, um fortzufahren.
Bazinga777
1
Hallo acdcjunior, wie spiegele ich den Webdienst, auf den ich zugreifen möchte?
Franva
2
@Franva Sie müssen einen HTTP-Server einrichten (z. B. Tomcat, Apache mit PHP, IIS mit ASP) und dort eine Seite platzieren, auf der für jede Anforderung ein Socket für den tatsächlichen Dienst (den Dienst, den Sie spiegeln) geöffnet wird. fordert die tatsächlichen Daten an und gibt sie dann als Antwort. Natürlich tun Sie dies über Code (Java, PHP, ASP usw.).
Acdcjunior
@acdcjunior Bitte korrigieren Sie mich, wenn mein Verständnis stimmt. Wenn ich eine URL direkt in den Browser eingebe, wird diese automatisch ohne Access-Control-Allow-Origin zur neuen Domain-URL umgeleitet . Wenn Sie beispielsweise WIF verwenden, wird der Benutzer beim ersten Anmelden zur Anmeldeseite eines Drittanbieters weitergeleitet.
Machinarium
@machinarium Ich bin mir nicht sicher, ob ich verstehe, was Sie gemeint haben, aber ich werde versuchen zu antworten (sagen Sie mir, wenn ich etwas falsch gemacht habe): Wenn Sie die URL in die Adressleiste des Browsers eingeben, ist das Vorhandensein oder Fehlen Access-Control-Allow-Origindieser URLs vorhanden Header spielen überhaupt keine Rolle - der Browser öffnet die URL wie gewohnt. Die Richtlinie mit demselben Ursprung (und die Anforderung für den Access-Control-Allow-OriginHeader) gilt nur für Ajax-Aufrufe.
Acdcjunior
29

Es gibt eine Art hack-tastischen Weg, dies zu tun, wenn Sie PHP auf Ihrem Server aktiviert haben. Ändern Sie diese Zeile:

url:   'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',

zu dieser Zeile:

url: '/path/to/phpscript.php',

und dann im PHP-Skript (wenn Sie die Berechtigung haben, die Funktion file_get_contents () zu verwenden):

<?php

header('Content-type: application/xml');
echo file_get_contents("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");

?>

Php scheint es nichts auszumachen, wenn diese URL von einem anderen Ursprung ist. Wie ich schon sagte, dies ist eine hackige Antwort, und ich bin sicher, dass etwas nicht stimmt, aber es funktioniert für mich.

Bearbeiten: Wenn Sie das Ergebnis in PHP zwischenspeichern möchten, ist hier die PHP-Datei, die Sie verwenden würden:

<?php

$cacheName = 'somefile.xml.cache';
// generate the cache version if it doesn't exist or it's too old!
$ageInSeconds = 3600; // one hour
if(!file_exists($cacheName) || filemtime($cacheName) > time() + $ageInSeconds) {
  $contents = file_get_contents('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml');
  file_put_contents($cacheName, $contents);
}

$xml = simplexml_load_file($cacheName);

header('Content-type: application/xml');
echo $xml;

?>

Caching-Code von hier nehmen .

Jyapayne
quelle
3
Eine noch bessere Lösung wäre, die XML-Datei auf der Serverseite zwischenzuspeichern und den file_get_contentsAufruf nur auszuführen , wenn die neueste XML-Datei ausreichend datiert ist. Vergessen Sie auch nicht Ihren Content-Type-Header :-)
sffc
Stolperte über diese Antwort. Frage: Woher weiß die PHP-Datei, dass sie die GET-Daten nimmt und an diese URL sendet? Würde dies auch mit POST-Daten funktionieren?
mpdc
Es reicht es nicht ein. Es ruft die Datei ab, speichert sie lokal und gibt sie dann aus, als wäre diese Datei die PHP-Datei, die Sie lokal anfordern. Es wird eine weitere Kopie davon abgerufen und gespeichert, wenn die lokal zwischengespeicherte Datei älter als das angegebene Höchstalter ist.
Dachte