Errore jQuery xml "Nessuna intestazione" Access-Control-Allow-Origin "presente sulla risorsa richiesta."


89

Sto lavorando a questo mio progetto personale solo per divertimento in cui voglio leggere un file xml che si trova su http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml e analizzare l'xml e usalo per convertire i valori tra le valute.

Finora ho escogitato il codice di seguito che è piuttosto semplice per leggere l'xml ma ottengo il seguente errore.

XMLHttpRequest non può caricare ****. Nessuna intestazione "Access-Control-Allow-Origin" è presente sulla risorsa richiesta. L'accesso a Origin " http://run.jsbin.com " non è pertanto consentito.

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

Non vedo nulla di sbagliato nel mio codice, quindi spero che qualcuno possa indicare cosa sto facendo di sbagliato con il mio codice e come potrei risolverlo.


2
Ti suggerisco di leggere la politica sulla stessa origine e CORS
jmoerdyk

l'errore indica esattamente cosa non va, parola per parola. Il tuo codice va bene, il problema è con il server a cui stai accedendo.
Kevin B

Risposte:


163

Non sarai in grado di effettuare una chiamata ajax http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xmlda un file distribuito a a http://run.jsbin.comcausa della politica della stessa origine .


Poiché la pagina di origine (ovvero l' origine ) e l' URL di destinazione si trovano in domini diversi ( run.jsbin.come www.ecb.europa.eu), il codice sta effettivamente tentando di effettuare una richiesta Cross-domain (CORS) , non ordinaria GET.

In poche parole, la politica della stessa origine afferma che i browser dovrebbero consentire solo le chiamate ajax ai servizi nello stesso dominio della pagina HTML.


Esempio:

Una pagina su http://www.example.com/myPage.htmlpuò richiedere direttamente solo servizi che si trovano in http://www.example.com, come http://www.example.com/api/myService. Se il servizio è ospitato in un altro dominio (ad esempio http://www.ok.com/api/myService), il browser non effettuerà la chiamata direttamente (come ci si aspetterebbe). Invece, proverà a fare una richiesta CORS.

In breve, per eseguire una richiesta (CORS) * su diversi domini, il tuo browser:

  • Includerà Originun'intestazione nella richiesta originale (con il dominio della pagina come valore) e la eseguirà come al solito; e poi
  • Solo se la risposta del server a tale richiesta contiene le intestazioni adeguate ( Access-Control-Allow-Originè una di queste ) che consente la richiesta CORS, la navigazione completerà la chiamata (quasi ** esattamente come sarebbe se la pagina HTML fosse nello stesso dominio).
    • Se le intestazioni previste non vengono visualizzate, il browser si arrende semplicemente (come ha fatto a te).


* Quanto sopra descrive i passaggi in una semplice richiesta, come un normale GETsenza intestazioni di fantasia. Se la richiesta non è semplice (come un POSTcon application/jsontipo di contenuto), il browser la tratterrà per un momento e, prima di soddisfarla, invierà prima una OPTIONSrichiesta all'URL di destinazione. Come sopra, continuerà solo se la risposta a questa OPTIONSrichiesta contiene le intestazioni CORS. Questa OPTIONSchiamata è nota come richiesta di verifica preliminare .
** Sto dicendo quasi perché ci sono altre differenze tra le chiamate normali e le chiamate CORS. Un aspetto importante è che alcune intestazioni, anche se presenti nella risposta, non verranno rilevate dal browser se non sono incluseAccess-Control-Expose-Headers nell'intestazione.


Come sistemarlo?

Era solo un errore di battitura? A volte il codice JavaScript ha solo un errore di battitura nel dominio di destinazione. Hai controllato? Se la pagina è all'indirizzo www.example.com, effettuerà solo chiamate regolari a www.example.com! Altri URL, come api.example.como anche example.como www.example.com:8080sono considerati domini diversi dal browser! Sì, se la porta è diversa, allora è un dominio diverso!

Aggiungi le intestazioni. Il modo più semplice per abilitare CORS è aggiungere le intestazioni necessarie (as Access-Control-Allow-Origin) alle risposte del server. (Ogni server / lingua ha un modo per farlo: controlla alcune soluzioni qui .)

Ultima risorsa: se non si dispone dell'accesso lato server al servizio, è anche possibile eseguirne il mirroring (tramite strumenti come i proxy inversi ) e includere tutte le intestazioni necessarie.


2
Grazie, sono molte informazioni. Ora posso eseguire le ricerche necessarie per procedere.
Bazinga777

1
Ciao acdcjunior, come faccio a eseguire il mirroring del servizio web a cui voglio accedere?
Franva

2
@Franva Dovrai impostare un server HTTP (es.Tomcat, Apache con PHP, IIS con ASP) e posizionare lì una pagina che, ad ogni richiesta, apre un socket al servizio vero e proprio (il servizio che stai specchiando), richiede i dati effettivi e quindi li fornisce come risposta. Ovviamente lo farai tramite codice (Java, PHP, ASP, ecc.).
acdcjunior

@acdcjunior Per favore correggimi se la mia comprensione è giusta. Se inserisco direttamente un URL nel browser, verrà reindirizzato automaticamente al nuovo URL di dominio senza Access-Control-Allow-Origin . Ad esempio, quando si utilizza WIF, l'utente verrà reindirizzato alla pagina di accesso di terze parti quando si accede per la prima volta.
machinarium

@machinarium Non sono sicuro di aver capito cosa intendevi, ma cercherò di rispondere (dimmi se ho sbagliato qualcosa): Se inserisci l'URL nella barra degli indirizzi del browser, la presenza o l'assenza di Access-Control-Allow-Originin quell'URL le intestazioni non avranno alcuna importanza: il browser aprirà l'URL come al solito. La politica della stessa origine (e il requisito per l' Access-Control-Allow-Originintestazione) si applica solo alle chiamate Ajax.
acdcjunior

29

C'è una sorta di modo hack-tastico per farlo se hai abilitato php sul tuo server. Cambia questa riga:

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

a questa riga:

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

e poi nello script php (se hai il permesso di usare la funzione file_get_contents ()):

<?php

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

?>

A Php non sembra importare se quell'URL proviene da un'origine diversa. Come ho detto, questa è una risposta hacky, e sono sicuro che ci sia qualcosa che non va, ma per me funziona.

Modifica: se vuoi memorizzare nella cache il risultato in php, ecco il file php che useresti:

<?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;

?>

Il codice di memorizzazione nella cache prende da qui .


3
Una soluzione ancora migliore sarebbe quella di memorizzare nella cache il file XML sul lato server ed eseguire la file_get_contentschiamata solo se il file XML più recente è sufficientemente datato. Inoltre, non dimenticare l'intestazione Content-Type :-)
sffc

Sono incappato in questa risposta. Domanda: come fa il file PHP a sapere di prendere i dati GET e inviarli a detto URL? Funzionerebbe anche con i dati POST?
mpdc

Non lo invia. Ottiene il file e lo salva localmente, quindi lo sputa come se quel file fosse il file php che stai richiedendo localmente. Recupererà e salverà un'altra copia se il file memorizzato nella cache locale è più vecchio dell'età massima specificata.
Pensiero
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.