Erreur jQuery xml 'Aucun en-tête' Access-Control-Allow-Origin 'n'est présent sur la ressource demandée.'

89

Je travaille sur ce projet personnel juste pour le plaisir où je veux lire un fichier xml qui se trouve à http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml et analyser le xml et utilisez-le pour convertir les valeurs entre les devises.

Jusqu'à présent, j'ai trouvé le code ci-dessous qui est assez basique pour lire le xml, mais j'obtiens l'erreur suivante.

XMLHttpRequest ne peut pas charger ****. Aucun en-tête «Access-Control-Allow-Origin» n'est présent sur la ressource demandée. L' accès à l' origine ' http://run.jsbin.com ' n'est donc pas autorisé.

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

Je ne vois rien de mal avec mon code, donc j'espère que quelqu'un pourra signaler ce que je fais de mal avec mon code et comment je pourrais le réparer.

Bazinga777
la source
2
Je vous suggère de lire sur la politique même origine et CORS
jmoerdyk
l'erreur indique exactement ce qui ne va pas, mot pour mot. Votre code est correct, le problème vient du serveur auquel vous accédez.
Kevin B
et voir aussi CORS sur MDN
Amir Ali Akbari

Réponses:

163

Vous ne pourrez pas passer un appel ajax à http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xmlpartir d'un fichier déployé à en http://run.jsbin.comraison de la politique de même origine .


Comme la page source (aka origine ) et l' URL cible se trouvent dans des domaines différents ( run.jsbin.comet www.ecb.europa.eu), votre code tente en fait de faire une requête interdomaine (CORS) , pas une requête ordinaire GET.

En quelques mots, la politique de même origine stipule que les navigateurs ne doivent autoriser que les appels ajax aux services du même domaine de la page HTML.


Exemple:

Une page sur http://www.example.com/myPage.htmlpeut uniquement demander directement les services qui sont à http://www.example.com, comme http://www.example.com/api/myService. Si le service est hébergé sur un autre domaine (par exemple http://www.ok.com/api/myService), le navigateur n'effectuera pas l'appel directement (comme vous vous en doutez). Au lieu de cela, il essaiera de faire une requête CORS.

En bref, pour effectuer une requête (CORS) * sur différents domaines, votre navigateur:

  • Inclura un en- Origintête dans la demande d'origine (avec le domaine de la page comme valeur) et l'exécutera comme d'habitude; puis
  • Ce n'est que si la réponse du serveur à cette requête contient les en- têtes adéquats ( Access-Control-Allow-Originest l' un d'entre eux ) permettant la requête CORS, la navigation terminera l'appel (presque ** exactement comme il le ferait si la page HTML était dans le même domaine).
    • Si les en-têtes attendus ne viennent pas, le navigateur abandonne simplement (comme il l'a fait pour vous).


* Ce qui précède décrit les étapes dans une requête simple , comme une requête régulière GETsans en-têtes sophistiqués. Si la demande n'est pas simple (comme un POSTavec application/jsoncomme type de contenu), le navigateur la retiendra un moment et, avant de la remplir, enverra d'abord une OPTIONSdemande à l'URL cible. Comme ci-dessus, il ne continuera que si la réponse à cette OPTIONSrequête contient les en-têtes CORS. Cet OPTIONSappel est appelé demande de contrôle en amont .
** Je dis presque parce qu'il existe d'autres différences entre les appels réguliers et les appels CORS. Un point important est que certains en-têtes, même s'ils sont présents dans la réponse, neAccess-Control-Expose-Headers seront pas récupérés par le navigateur s'ils ne sont pas inclus dans l'en- tête.


Comment le réparer?

Était-ce juste une faute de frappe? Parfois, le code JavaScript n'a qu'une faute de frappe dans le domaine cible. Avez-vous vérifié? Si la page est à, www.example.comil ne fera que des appels réguliers à www.example.com! D'autres URL, telles que api.example.comou même example.comou www.example.com:8080sont considérées comme des domaines différents par le navigateur! Oui, si le port est différent, alors c'est un domaine différent!

Ajoutez les en-têtes. Le moyen le plus simple d' activer CORS consiste à ajouter les en-têtes nécessaires (as Access-Control-Allow-Origin) aux réponses du serveur. (Chaque serveur / langue a un moyen de le faire - vérifiez quelques solutions ici .)

Dernier recours: si vous ne disposez pas d'un accès côté serveur au service, vous pouvez également le mettre en miroir (via des outils tels que des proxys inverses ) et y inclure tous les en-têtes nécessaires.

acdcjunior
la source
2
Merci, c'est beaucoup d'informations. Maintenant, je peux effectuer les recherches nécessaires pour continuer.
Bazinga777
1
Salut acdcjunior, comment mettre en miroir le service Web auquel je souhaite accéder?
Franva
2
@Franva Vous devrez configurer un serveur HTTP (par exemple Tomcat, Apache avec PHP, IIS avec ASP) et y placer une page qui, pour chaque requête, ouvre une socket vers le service réel (le service que vous mettez en miroir), demande les données réelles et les donne ensuite en réponse. Bien sûr, vous le ferez via du code (Java, PHP, ASP, etc.).
acdcjunior
@acdcjunior Veuillez me corriger si je comprends bien. Si j'entre une URL directement dans le navigateur, elle redirigera automatiquement vers la nouvelle URL de domaine sans Access-Control-Allow-Origin . Par exemple, lors de l'utilisation de WIF, l'utilisateur sera redirigé vers la page de connexion tierce lors de la première connexion.
machinarium
@machinarium Je ne suis pas sûr de comprendre ce que vous vouliez dire, mais je vais essayer de répondre (dites-moi si je me suis trompé): Si vous entrez l'URL dans la barre d'adresse du navigateur, la présence ou l'absence de Access-Control-Allow-Origindans cette URL les en-têtes n'auront aucune importance - le navigateur ouvrira l'URL comme d'habitude. La politique de même origine (et l'exigence de l'en- Access-Control-Allow-Origintête) ne s'applique qu'aux appels Ajax.
acdcjunior
29

Il existe une sorte de moyen hack-tastic de le faire si php est activé sur votre serveur. Changez cette ligne:

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

à cette ligne:

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

puis dans le script php (si vous avez l'autorisation d'utiliser la fonction file_get_contents ()):

<?php

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

?>

Php ne semble pas déranger si cette URL est d'origine différente. Comme je l'ai dit, c'est une réponse hacky, et je suis sûr qu'il y a quelque chose qui ne va pas, mais cela fonctionne pour moi.

Edit: Si vous souhaitez mettre en cache le résultat en php, voici le fichier php que vous utiliseriez:

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

?>

Le code de mise en cache prend ici .

jyapayne
la source
3
Une solution encore meilleure serait de mettre en cache le fichier XML côté serveur et de n'effectuer l' file_get_contentsappel que si le fichier XML le plus récent est suffisamment daté. N'oubliez pas non plus votre en-tête Content-Type :-)
sffc
Je suis tombé sur cette réponse. Question: Comment le fichier PHP sait-il prendre les données GET et les soumettre à ladite URL? Cela fonctionnerait-il également avec les données POSTées?
mpdc
Il ne le soumet pas. Il récupère le fichier et l'enregistre localement, puis le crache comme si ce fichier était le fichier php que vous demandez localement. Il en récupérera et en enregistrera une autre copie si le fichier mis en cache localement est plus ancien que l'âge maximum indiqué.
Pensé