Error XmlHttpRequest: Access-Control-Allow-Origin no permite el origen nulo

550

Estoy desarrollando una página que extrae imágenes de Flickr y Panoramio a través del soporte AJAX de jQuery.

El lado de Flickr funciona bien, pero cuando trato $.get(url, callback)de Panoramio, veo un error en la consola de Chrome:

XMLHttpRequest no puede cargar 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 . El origen nulo no está permitido por Access-Control-Allow-Origin.

Si consulto esa URL desde un navegador directamente, funciona bien. ¿Qué está pasando y puedo evitar esto? ¿Estoy redactando mi consulta incorrectamente, o es algo que Panoramio hace para obstaculizar lo que estoy tratando de hacer?

Google no mostró coincidencias útiles en el mensaje de error .

EDITAR

Aquí hay un código de muestra que muestra el problema:

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

Puede ejecutar el ejemplo en línea .

EDITAR 2

Gracias a Darin por su ayuda con esto. EL CÓDIGO ANTERIOR ESTÁ MAL. Use esto en su lugar:

$().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...
  });
});
Drew Noakes
fuente
1
¿Cómo se ve la URL desde la que realiza la solicitud ? Esto no sucede ser un generado dinámicamente en el iframeque usted document.write, por ejemplo?
Pekka
55
¿Puede publicar la respuesta HTTP de la solicitud en cada servicio? Apuesto a que Panoramio no está sirviendo Access-Control-Allow-Origin. Consulte w3.org/TR/cors para ver ejemplos.
Kevin Hakanson
2
@ Kevin, no necesitas esos encabezados si el servidor envía JSONP.
Darin Dimitrov
@Pekka, estoy ejecutando la página desde mi máquina local en este momento ( file:///C:/). No iframeestá involucrado.
Drew Noakes
1
@drew ¿qué sucede si lo ejecutas desde una URL http? No debería hacer una diferencia de esta manera, sino solo excluir la posibilidad.
Pekka

Respuestas:

423

Para el registro, por lo que puedo decir, tuviste dos problemas:

  1. No estaba pasando un especificador de tipo "jsonp" a su $.get, por lo que estaba usando un XMLHttpRequest ordinario. Sin embargo, su navegador admite CORS (Cross-Origin Resource Sharing) para permitir XMLHttpRequest entre dominios si el servidor lo autorizó. Ahí es donde Access-Control-Allow-Originentró el encabezado.

  2. Creo que mencionaste que lo estabas ejecutando desde un archivo: // URL. Hay dos formas para que los encabezados CORS indiquen que un XHR de dominio cruzado está bien. Una es enviar Access-Control-Allow-Origin: *(que, si estaba llegando a Flickr a través de $.get, deben haber estado haciendo) mientras que la otra era hacer eco de los contenidos del Originencabezado. Sin embargo, las file://URL producen un valor nulo Originque no se puede autorizar mediante eco-back.

El primero fue resuelto de forma indirecta por la sugerencia de Darin de usar $.getJSON. Hace un poco de magia cambiar el tipo de solicitud de su valor predeterminado de "json" a "jsonp" si ve la subcadena callback=?en la URL.

Eso resolvió el segundo al no tratar de realizar una solicitud CORS desde una file://URL.

Para aclarar a otras personas, aquí están las sencillas instrucciones de solución de problemas:

  1. Si está intentando usar JSONP, asegúrese de que uno de los siguientes es el caso:
    • Usted está utilizando $.gety establecer dataTypea jsonp.
    • Estás utilizando $.getJSONe incluido callback=?en la URL.
  2. Si está intentando hacer una XMLHttpRequest entre dominios a través de CORS ...
    1. Asegúrate de probar a través de http://. Los scripts que se ejecutan a través de file://tienen soporte limitado para CORS.
    2. Asegúrese de que el navegador realmente sea compatible con CORS . (Opera e Internet Explorer llegan tarde a la fiesta)
ssokolow
fuente
45
Entonces, ¿cuál es la solución a esto?
jQuerybeast
19
devolución de llamada =? FTW
Tim
10
Algunos navegadores como Chrome permiten CORS si se inicia con el parámetro --allow-file-access-from-files
echox
1
callback=?no funcionó para mí, pero lo jsonp=?hizo. ¿Alguna explicación para eso?
crunkchitis
1
¿Tengo que instalar un servidor web para probar http://? ¿O hay una manera de simplemente abrir el archivo usando ese protocolo?
Will Sewell
76

Quizás necesite agregar un HEADER en su script llamado, esto es lo que tuve que hacer en PHP:

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

Más detalles en Dominio cruzado AJAX o servicios WEB (en francés).

Thomas Decaux
fuente
1
@Uri: depende de su servidor HTTP. Con Apache, querrás buscar mod_headers.
ssokolow
44
@Uri <meta http-equiv = "Access-Control-Allow-Origin" content = "*">
Herberth Amaral
77
@HerberthAmaral Intenté agregar esto dentro de <head> </head>, pero no funciona para mí. Lo estoy probando en iOS Safari y Chrome, pero en la consola, obtengo el origen nulo no permitido por el error Access-Control-Allow-Origin.
thandasoru
3
No funciona en la cabecera del archivo html por razones de seguridad. Tiene que ser un encabezado. stackoverflow.com/questions/7015782/…
Justin Blank
No puede estar en el HTML. Tiene que ser especificado por el programa que envía la página HTML a su navegador a través de la red (denominado servidor web).
Roman Plášil
70

Para un proyecto HTML simple:

cd project
python -m SimpleHTTPServer 8000

Luego busque su archivo.

CodeGroover
fuente
1
mientras es increíble y funciona, cuando va a 0.0.0.0:8000 y prueba las POSTsolicitudes que obtiene: code 501, message Unsupported method ('POST')para los google.
pjammer
"Cuando un servidor web Python (como cherrypy, por ejemplo) dice que está sirviendo en 0.0.0.0 significa que está escuchando todo el tráfico TCP que termina en esa máquina sin importar el nombre de host o IP que se solicitó". Así que quizás intente publicar en localhost: 8000 stackoverflow.com/a/4341808/102022
eric.christensen
20

Funciona para mí en Google Chrome v5.0.375.127 (recibo la alerta):

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

También te recomendaría usar el $.getJSON()método, ya que el anterior no funciona en IE8 (al menos en mi máquina):

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

Puede probarlo en línea desde aquí .


ACTUALIZAR:

Ahora que ha mostrado su código, puedo ver el problema con él. Tiene una función anónima y una función en línea, pero ambas serán llamadas processImages. Así es como funciona el soporte JSONP de JQuery. Observe cómo estoy definiendo el callback=?para que pueda usar una función anónima. Puede leer más al respecto en la documentación .

Otro comentario es que no debes llamar a eval. El parámetro pasado a su función anónima ya será analizado en JSON por jQuery.

Darin Dimitrov
fuente
Hmm ok, déjame intentarlo de nuevo. Estoy en una versión diferente de Chrome por cierto "6.0.472.51 beta".
Drew Noakes
Tu código funcionó para mí. Sin embargo, actualicé mi pregunta con un código que dibuja el problema.
Drew Noakes el
Gracias por el consejo sobre getJSON ... Bifurqué su ejemplo jsFiddle para mostrar el problema ( jsfiddle.net/ZfvKm ) y ahora veo el mensaje de error XMLHttpRequest no puede cargar panoramio.com/wapi/data/… . Origin fiddle.jshell.net no está permitido por Access-Control-Allow-Origin.
Drew Noakes el
@ Darin, gracias por la actualización. Veo su punto, y he jugado con algunas combinaciones de esto, pero aún no he encontrado una manera de acceder al objeto devuelto. ¿Podría actualizar su ejemplo jsFiddle para mostrar el acceso a los datos? Si configura la devolución de llamada a, ?entonces el JSON devuelto está rodeado de paréntesis. No define un parámetro para su función de devolución de llamada, y el valor de thisno parece tener un objeto de respuesta (al menos, el .datavalor es null).
Drew Noakes el
@Darin, fantástico. Muchas gracias. Trabajando bien ahora. Para cualquiera que lea esto, la inclusión de callback=?le dice a jQuery que genere un nombre de función aleatorio internamente, lo que resulta en una llamada a la función anónima a la que pasa .getJSON. Apreciado.
Drew Noakes
8

Mientras el servidor solicitado sea compatible con el formato de datos JSON, use la interfaz JSONP (JSON Padding). Le permite realizar solicitudes de dominio externo sin servidores proxy o encabezados sofisticados.

Cheng Chen
fuente
4

Lo gestionamos a través del http.confarchivo (editado y luego reiniciamos el servicio 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>

En el Header set Access-Control-Allow-Origin "*", puede poner una URL precisa.

romu31
fuente
1
Esto no funciona en Apache de XAMPP. El problema aún existe.
Raptor
1
Esto no es seguro. Vea aquí por qué; stackoverflow.com/questions/7564832/…
Rob
No es seguro como dijo @RobQuist.
d337
4

Si está haciendo pruebas locales o llamando al archivo desde algo como file:// entonces necesita deshabilitar la seguridad del navegador.

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

user2701060
fuente
3

En mi caso, el mismo código funcionó bien en Firefox, pero no en Google Chrome. La consola JavaScript de Google Chrome dijo:

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"

Tuve que soltar la parte www de la URL de Ajax para que coincida correctamente con la URL de origen y funcionó bien entonces.

Kalpesh Patel
fuente
2

No todos los servidores admiten jsonp. Requiere que el servidor configure la función de devolución de llamada en sus resultados. Lo uso para obtener respuestas json de sitios que devuelven json puro pero no admiten jsonp:

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;
    });
}
mAsT3RpEE
fuente
1

Yo uso el servidor Apache, así que he usado el módulo mod_proxy. Habilitar módulos:

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

Luego añade:

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

Finalmente, pase proxy-url a su script.

zenio
fuente
1

Como nota final, la documentación de Mozilla dice explícitamente que

El ejemplo anterior fallaría si el encabezado se marcara como: Access-Control-Allow-Origin: *. Como Access-Control-Allow-Origin menciona explícitamente http: //foo.example , el contenido que reconoce las credenciales se devuelve al contenido web que se invoca.

Como consecuencia, no es simplemente una mala práctica usar '*'. Simplemente no funciona :)

usuario2688838
fuente
1

Para PHP: esto funciona para mí en Chrome, Safari y Firefox

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

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

usando axios llame a php live services con file: //

Tyler
fuente
Esto también funciona de mí, observe cómo nulo es un STRING y no un NULL real. Lo uso ya header('Access-Control-Allow-Origin: '.( trim($_SERVER['HTTP_REFERER'],'/') ?:'null'),true);que permite el origen cruzado de un servidor remoto a otro también y caigo en (cadena) nulo para el archivo local.
Louis Loudog Trottier
0

También recibí el mismo error en Chrome (no probé otros navegadores). Fue debido al hecho de que estaba navegando en domain.com en lugar de www.domain.com. Un poco extraño, pero podría resolver el problema agregando las siguientes líneas a .htaccess. Redirige domain.com a www.domain.com y se solucionó el problema. Soy un visitante web perezoso, por lo que casi nunca escribo el www, pero aparentemente en algunos casos es obligatorio.

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

Asegúrese de estar utilizando la última versión de JQuery. Nos enfrentamos a este error para JQuery 1.10.2 y el error se resolvió después de usar JQuery 1.11.1

Ganesh Kamath - 'Code Frenzy'
fuente
0

Amigos

Me encontré con un problema similar. Pero usando Fiddler, pude llegar al problema. El problema es que la URL del cliente que está configurada en la implementación de CORS en el lado de la API web no debe tener una barra diagonal final. Después de enviar su solicitud a través de Google Chrome e inspeccionar la pestaña TextView de la sección Encabezados de Fiddler, el mensaje de error indica algo como esto:

* "El origen de política especificado your_client_url: / 'no es válido. No puede terminar con una barra diagonal".

Esto es realmente peculiar porque funcionó sin problemas en Internet Explorer, pero me dio dolor de cabeza al probar con Google Chrome.

Eliminé la barra diagonal en el código CORS y volví a compilar la API web, y ahora se puede acceder a la API a través de Chrome e Internet Explorer sin ningún problema. Por favor, dale una oportunidad.

Gracias Andy

andymenon
fuente
0

Hay un pequeño problema en la solución publicada por CodeGroover arriba , donde si cambia un archivo, tendrá que reiniciar el servidor para usar realmente el archivo actualizado (al menos, en mi caso).

Así que buscando un poco, encontré este para usar:

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

Y luego servirá a las http://localhost:8000.

MiJyn
fuente
1
Si bien este enlace puede responder la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia. - De la opinión
Vi100
Aclarado el enlace.
MiJyn