Error de jQuery XML "No hay ningún encabezado 'Access-Control-Allow-Origin' presente en el recurso solicitado".
Estoy trabajando en este proyecto personal mío sólo por diversión, donde quiero leer un archivo xml que se encuentra en http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml y analizar el xml y Úselo para convertir valores entre las monedas.
Hasta ahora he creado el siguiente código, que es bastante básico para leer el XML, pero aparece el siguiente error.
XMLHttpRequest no puede cargar ****. No hay ningún encabezado 'Access-Control-Allow-Origin' en el recurso solicitado. Por lo tanto, no se permite el acceso al origen 'http://run.jsbin.com'.
$(document).ready(
function() {
$.ajax({
type: 'GET',
url: 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',
dataType: 'xml',
success: function(xml){
alert('aaa');
}
});
}
);
No veo nada malo en mi código, así que espero que alguien pueda señalar qué estoy haciendo mal con mi código y cómo podría solucionarlo.
No podrá realizar una llamada ajax http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml
desde un archivo implementado http://run.jsbin.com
debido a la política del mismo origen .
Como la página de origen (también conocida como origen ) y la URL de destino están en dominios diferentes ( run.jsbin.com
y www.ecb.europa.eu
), su código en realidad está intentando realizar una solicitud entre dominios (CORS) , no una solicitud ordinaria GET
.
En pocas palabras, la política del mismo origen dice que los navegadores sólo deben permitir llamadas ajax a servicios en el mismo dominio de la página HTML.
Ejemplo:
Una página en http://www.example.com/myPage.html
solo puede solicitar directamente servicios que se encuentren en http://www.example.com
, como http://www.example.com/api/myService
. Si el servicio está alojado en otro dominio (por ejemplo http://www.ok.com/api/myService
), el navegador no realizará la llamada directamente (como era de esperar). En cambio, intentará realizar una solicitud CORS.
En pocas palabras, para realizar una solicitud (CORS)* en diferentes dominios, su navegador:
- Incluirá un
Origin
encabezado en la solicitud original (con el dominio de la página como valor) y lo realizará como de costumbre; y luego - Solo si la respuesta del servidor a esa solicitud contiene los encabezados adecuados (
Access-Control-Allow-Origin
es uno de ellos ) que permiten la solicitud CORS, la navegación completará la llamada (casi** exactamente como lo haría si la página HTML estuviera en el mismo dominio).- Si los encabezados esperados no aparecen, el navegador simplemente se da por vencido (como le ocurrió a usted).
* Lo anterior muestra los pasos de una solicitud simple , como una normal GET
sin encabezados sofisticados. Si la solicitud no es simple (como un tipo de contenido POST
con application/json
as), el navegador la retendrá por un momento y, antes de cumplirla, primero enviará una OPTIONS
solicitud a la URL de destino. Como arriba, solo continuará si la respuesta a esta OPTIONS
solicitud contiene los encabezados CORS. Esta OPTIONS
llamada se conoce como solicitud de verificación previa .
** Digo casi porque existen otras diferencias entre las llamadas regulares y las llamadas CORS. Una importante es que algunos encabezados, incluso si están presentes en la respuesta, no serán detectados por el navegador si no están incluidos en elAccess-Control-Expose-Headers
encabezado.
¿Como arreglarlo?
¿Fue solo un error tipográfico? A veces, el código JavaScript solo tiene un error tipográfico en el dominio de destino. ¿Te fijaste? Si la página está en www.example.com
sólo hará llamadas regulares a www.example.com
! ¡Otras URL, como api.example.com
o incluso example.com
o, www.example.com:8080
son consideradas dominios diferentes por el navegador! Sí, si el puerto es diferente, ¡entonces es un dominio diferente!
Agrega los encabezados. La forma más sencilla de habilitar CORS es agregando los encabezados necesarios (como Access-Control-Allow-Origin
) a las respuestas del servidor. (Cada servidor/idioma tiene una forma de hacerlo; consulte algunas soluciones aquí ).
Último recurso: si no tiene acceso al servicio desde el lado del servidor, también puede duplicarlo (a través de herramientas como servidores proxy inversos ) e incluir todos los encabezados necesarios allí.
Hay una forma fantástica de hacerlo si tienes php habilitado en tu servidor. Cambie esta línea:
url: 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',
a esta línea:
url: '/path/to/phpscript.php',
y luego en el script php (si tiene permiso para usar la función 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 no parece importarle si esa URL es de un origen diferente. Como dije, esta es una respuesta engañosa y estoy seguro de que tiene algún problema, pero funciona para mí.
Editar: si desea almacenar en caché el resultado en php, aquí está el archivo php que usaría:
<?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;
?>
El código de almacenamiento en caché se toma de aquí .