2011-04-21 17 views
159

Tengo una máquina en mi LAN local (machineA) que tiene dos servidores web. El primero es el integrado en XBMC (en el puerto 8080) y muestra nuestra biblioteca. El segundo servidor es un script python CherryPy (puerto 8081) que estoy usando para activar una conversión de archivos a pedido. La conversión de archivos se desencadena mediante una solicitud AJAX POST desde la página servida desde el servidor XBMC.Cómo obtener una solicitud de intercambio de recursos de origen cruzado (CORS) que funciona

  • Goto http://machineA:8080 que muestra la biblioteca
  • Biblioteca se muestra
  • usuario hace clic en 'convertir' eslabón que emite el siguiente comando -

jQuery Ajax Solicitud

$.post('http://machineA:8081', {file_url: 'asfd'}, function(d){console.log(d)}) 
  • El navegador emite un HT TP OPCIONES solicita con los siguientes encabezados;

encabezado de solicitud - OPCIONES

Host: machineA:8081 
User-Agent: ... Firefox/4.01 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: en-us,en;q=0.5 
Accept-Encoding: gzip,deflate 
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 
Keep-Alive: 115 
Connection: keep-alive 
Origin: http://machineA:8080 
Access-Control-Request-Method: POST 
Access-Control-Request-Headers: x-requested-with 
  • El servidor responde con el siguiente;

encabezado de respuesta - OPCIONES (STATUS = 200 OK)

Content-Length: 0 
Access-Control-Allow-Headers: * 
Access-Control-Max-Age: 1728000 
Server: CherryPy/3.2.0 
Date: Thu, 21 Apr 2011 22:40:29 GMT 
Access-Control-Allow-Origin: * 
Access-Control-Allow-Methods: POST, GET, OPTIONS 
Content-Type: text/html;charset=ISO-8859-1 
  • La conversación se detiene. El navegador, en teoría, debe emitir una solicitud POST ya que el servidor respondió con los encabezados CORS correctos (? Access-Control-Allow-Origin: *)

Para la solución de problemas, también emití los mismos $ comando .post desde http://jquery.com. Aquí es donde estoy perplejo, desde jquery.com, la solicitud de publicación funciona, una solicitud de OPCIONES se envía después de un POST. Los encabezados de esta transacción están debajo;

encabezado de solicitud - OPCIONES

Host: machineA:8081 
User-Agent: ... Firefox/4.01 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: en-us,en;q=0.5 
Accept-Encoding: gzip,deflate 
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 
Keep-Alive: 115 
Connection: keep-alive 
Origin: http://jquery.com 
Access-Control-Request-Method: POST 

Respuesta Header - OPCIONES (STATUS = 200 OK)

Content-Length: 0 
Access-Control-Allow-Headers: * 
Access-Control-Max-Age: 1728000 
Server: CherryPy/3.2.0 
Date: Thu, 21 Apr 2011 22:37:59 GMT 
Access-Control-Allow-Origin: * 
Access-Control-Allow-Methods: POST, GET, OPTIONS 
Content-Type: text/html;charset=ISO-8859-1 

encabezado de solicitud - Publique

Host: machineA:8081 
User-Agent: ... Firefox/4.01 
Accept: */* 
Accept-Language: en-us,en;q=0.5 
Accept-Encoding: gzip,deflate 
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 
Keep-Alive: 115 
Connection: keep-alive 
Content-Type: application/x-www-form-urlencoded; charset=UTF-8 
Referer: http://jquery.com/ 
Content-Length: 12 
Origin: http://jquery.com 
Pragma: no-cache 
Cache-Control: no-cache 

Respuesta Header - POST (STATUS = 200 OK)

Content-Length: 32 
Access-Control-Allow-Headers: * 
Access-Control-Max-Age: 1728000 
Server: CherryPy/3.2.0 
Date: Thu, 21 Apr 2011 22:37:59 GMT 
Access-Control-Allow-Origin: * 
Access-Control-Allow-Methods: POST, GET, OPTIONS 
Content-Type: application/json 

No puedo entender por qué la misma petición funcionaría de un sitio, pero no el otro. Espero que alguien pueda señalar lo que me estoy perdiendo. ¡Gracias por tu ayuda!

+0

Se necesita CORS si ambos servidores web están en el mismo equipo? – jdigital

+6

Según mi leal saber y entender, se trata de una solicitud CORS debido al puerto diferente. Además, la solicitud OPTIONS indica que el navegador lo está tratando como una solicitud CORS – James

+0

Posible duplicado de [Una solicitud CORS POST funciona desde JavaScript simple, pero ¿por qué no con jQuery?] (Https://stackoverflow.com/questions/5584923/ a-cors-post-request-works-from-plain-javascript-but-why-not-with-jquery) – Liam

Respuesta

126

finalmente me encontré con este enlace "A CORS POST request works from plain javascript, but why not with jQuery?" que señala que jQuery 1.5.1 añade la cabecera

Access-Control-Request-Headers: x-requested-with 

a todas las solicitudes CORS. jQuery 1.5.2 no hace esto. Además, según la misma pregunta, establecer un encabezado de respuesta del servidor de

Access-Control-Allow-Headers: * 

no permite que la respuesta continúe. Debe asegurarse de que el encabezado de respuesta incluya específicamente los encabezados necesarios. es decir:

Access-Control-Allow-Headers: x-requested-with 
48

SOLICITUD:

$.ajax({ 
      url: "http://localhost:8079/students/add/", 
      type: "POST", 
      crossDomain: true, 
      data: JSON.stringify(somejson), 
      dataType: "json", 
      success: function (response) { 
       var resp = JSON.parse(response) 
       alert(resp.status); 
      }, 
      error: function (xhr, status) { 
       alert("error"); 
      } 
     }); 

RESPUESTA:

response = HttpResponse(json.dumps('{"status" : "success"}')) 
response.__setitem__("Content-type", "application/json") 
response.__setitem__("Access-Control-Allow-Origin", "*") 

return response 
+3

si el servidor no acepta origen cruzado, crossdomain = true no tiene que resolver el problema. utilizando dataType: "jsonp" y configurando la devolución de llamada como jsonpCallback: "response" sería la mejor idea para hacer esto. Consulte también: http://api.jquery.com/jquery.ajax/ – BonifatiusK

+18

jsonp no funciona para la solicitud POST –

3

El uso de este en combinación con laravel resuelto mi problema. Simplemente agregue este encabezado a su solicitud de jquery Access-Control-Request-Headers: x-requested-with y asegúrese de que su respuesta del lado del servidor tenga este encabezado configurado en Access-Control-Allow-Headers: *.

+3

No hay motivo para agregar encabezados CORS a la solicitud manualmente. El navegador siempre agregará los encabezados CORS prop a la solicitud para usted. –

+0

El verdadero desafío es conseguir que el servidor responda con un 'Control de acceso-Permitir encabezados' y JQ correctos que proporcionen 'Cabeceras de solicitud de control de acceso' correctas (además de cualquier código que agregue mediante el código) ninguno de los cuales puede ser comodines. solo se necesita un encabezado "malo" para volar el pre-vuelo, p. usando 'If-None-Match' para un GET condicional, si el servidor no tiene esa lista. –

8

Bueno, luché con este problema durante un par de semanas.

La forma más fácil, más compatible y no hacky de hacer esto es probablemente utilizar un proveedor de API de JavaScript que no realiza llamadas basadas en el navegador y puede manejar las solicitudes Cross Origin.

E.g. Facebook JavaScript API y Google JS API.

En caso de que su proveedor de API no sea actual y no admita el encabezado Cross Origin Resource Origin '*' en su respuesta y no tenga una API JS (Sí, estoy hablando de usted Yahoo), se sorprenderá con una de tres opciones:

  1. Uso de jsonp en sus solicitudes, que agrega una función de devolución de llamada a su URL donde puede manejar su respuesta. Advertencia: esto cambiará la URL de solicitud para que su servidor API esté equipado para gestionar la devolución de llamada = al final de la URL.

  2. Envíe la solicitud al servidor API que controla usted y está en el mismo dominio que el cliente o tiene Cross Origin Resource Sharing habilitado desde donde puede proxy la solicitud al servidor API de terceros.

  3. Probablemente más útil en los casos en los que realiza solicitudes de OAuth y necesita manejar la interacción del usuario ¡Jaja!window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')

1

Este es un resumen de lo que funcionó para mí:

definir una nueva función (envuelto $.ajax para simplificar):

jQuery.postCORS = function(url, data, func) { if(func == undefined) func = function(){}; return $.ajax({ type: 'POST', url: url, data: data, dataType: 'json', contentType: 'application/x-www-form-urlencoded', xhrFields: { withCredentials: true }, success: function(res) { func(res) }, error: function() { func({}) }}); } 

Uso:

$.postCORS("https://example.com/service.json",{ x : 1 },function(obj){ 
     if(obj.ok) { 
      ... 
     } 
}); 

también funciona con .done, .fail, etc .:

$.postCORS("https://example.com/service.json",{ x : 1 }).done(function(obj){ 
     if(obj.ok) { 
      ... 
     } 
}).fail(function(){ 
    alert("Error!"); 
}); 

lado del servidor (en este caso en el que está instalado example.com), establecer estas cabeceras (añadido algunos ejemplos de código en PHP):

header('Access-Control-Allow-Origin: https://not-example.com'); 
header('Access-Control-Allow-Credentials: true'); 
header('Access-Control-Max-Age: 604800'); 
header("Content-type: application/json"); 
$array = array("ok" => $_POST["x"]); 
echo json_encode($array); 

Esta es la única forma que conozco de verdad la POST dominio cruzado de JS.

JSONP convierte la POST en GET que puede mostrar información confidencial en los registros del servidor.

-4

Esto es un poco tarde para la fiesta, pero he estado luchando con esto por un par de días. Es posible y ninguna de las respuestas que encontré aquí ha funcionado. Es engañosamente simple. Aquí está la llamada .ajax:

<!DOCTYPE HTML> 
 
    <html> 
 
    <head> 
 
    <body> 
 
    <title>Javascript Test</title> 
 
    <script src="http://code.jquery.com/jquery-latest.min.js"></script> 
 
    <script type="text/javascript"> 
 
    $(document).domain = 'XXX.com'; 
 
    $(document).ready(function() { 
 
    $.ajax({ 
 
     xhrFields: {cors: false}, 
 
     type: "GET", 
 
     url: "http://XXXX.com/test.php?email='[email protected]'", 
 
     success: function (data) { 
 
      alert(data); 
 
     }, 
 
     error: function (x, y, z) { 
 
      alert(x.responseText + " :EEE: " + x.status); 
 
     } 
 
    }); 
 
    }); 
 
    </script> 
 
    </body> 
 
    </html>

Aquí está el php en el lado del servidor:

<html> 
 
    <head> 
 
    <title>PHP Test</title> 
 
    </head> 
 
    <body> 
 
     <?php 
 
     header('Origin: xxx.com'); 
 
     header('Access-Control-Allow-Origin:*'); 
 
     $servername = "sqlxxx"; 
 
     $username = "xxxx"; 
 
     $password = "sss"; 
 
     $conn = new mysqli($servername, $username, $password); 
 
     if ($conn->connect_error) { 
 
     die("Connection failed: " . $conn->connect_error); 
 
     } 
 
     $sql = "SELECT email, status, userdata FROM msi.usersLive"; 
 
     $result = $conn->query($sql); 
 
     if ($result->num_rows > 0) { 
 
     while($row = $result->fetch_assoc()) { 
 
     echo $row["email"] . ":" . $row["status"] . ":" . $row["userdata"] . "<br>"; 
 
     } 
 
    } else { 
 
     echo "{ }"; 
 
    } 
 
    $conn->close(); 
 
    ?> 
 
    </body>

+1

Por lo que vale, el encabezado Origen es un encabezado de solicitud, no un encabezado de respuesta. Su script php no debería estar configurándolo. – Noodles

+0

Hmmph. Esto hizo que mis esperanzas subieran y luego se desvaneció cuando vi que estás haciendo un AJAX "GET" en tu código, cuando el OP dijo claramente que estaba tratando de EVITAR usar "GET" y quería usar "POST". –

+0

Los encabezados CORS funcionan igual independientemente del verbo involucrado. Podemos invocar todos los verbos a través de '$ .ajax()' contra un servidor configurado correctamente. La parte más difícil es hacer que 'Access-Control-Request-Headers' sea correcto, pero incluso eso no es demasiado difícil. Como se señaló en los carteles anteriores, este no debe ser un comodín, sino una lista blanca de encabezados. –

6

Me tomó algún tiempo para encontrar la solución.

En caso de que su respuesta del servidor correctamente y que la petición es el problema, se debe añadir a la withCredentials: truexhrFields en la solicitud:

$.ajax({ 
    url: url, 
    type: method, 
    // This is the important part 
    xhrFields: { 
     withCredentials: true 
    }, 
    // This is the important part 
    data: data, 
    success: function (response) { 
     // handle the response 
    }, 
    error: function (xhr, status) { 
     // handle errors 
    } 
}); 

Nota: jQuery> = 1.5.1 se requerido

+0

Todavía no funciona para mí :( –

+0

jquery version ok? ¿Estableciste 'conCredentials: true'? ¿Seguro que tienes los encabezados relevantes? – Dekel

+0

Sí, 1withCredential: true',' jquery version: 3.2.1'. De hecho, está trabajando a través del cartero, pero no está pasando por el navegador Chrome –

0

Por alguna razón, una pregunta sobre las solicitudes GET se fusionó con esta, por lo que voy a responder aquí.

Esta sencilla función obtendrá asíncronamente una respuesta de estado HTTP desde una página habilitada para CORS. Si lo ejecuta, verá que solo una página con los encabezados adecuados devuelve un estado de 200 si se accede a través de XMLHttpRequest, ya sea que se use GET o POST. No se puede hacer nada en el lado del cliente para evitar esto, excepto posiblemente usando JSONP si solo necesita un objeto json.

A continuación se puede modificar fácilmente para obtener los datos almacenados en el objeto xmlHttpRequestObject:

function checkCorsSource(source) { 
 
    var xmlHttpRequestObject; 
 
    if (window.XMLHttpRequest) { 
 
    xmlHttpRequestObject = new XMLHttpRequest(); 
 
    if (xmlHttpRequestObject != null) { 
 
     var sUrl = ""; 
 
     if (source == "google") { 
 
     var sUrl = "https://www.google.com"; 
 
     } else { 
 
     var sUrl = "https://httpbin.org/get"; 
 
     } 
 
     document.getElementById("txt1").innerHTML = "Request Sent..."; 
 
     xmlHttpRequestObject.open("GET", sUrl, true); 
 
     xmlHttpRequestObject.onreadystatechange = function() { 
 
     if (xmlHttpRequestObject.readyState == 4 && xmlHttpRequestObject.status == 200) { 
 
      document.getElementById("txt1").innerHTML = "200 Response received!"; 
 
     } else { 
 
      document.getElementById("txt1").innerHTML = "200 Response failed!"; 
 
     } 
 
     } 
 
     xmlHttpRequestObject.send(); 
 
    } else { 
 
     window.alert("Error creating XmlHttpRequest object. Client is not CORS enabled"); 
 
    } 
 
    } 
 
}
<html> 
 
<head> 
 
    <title>Check if page is cors</title> 
 
</head> 
 
<body> 
 
    <p>A CORS-enabled source has one of the following HTTP headers:</p> 
 
    <ul> 
 
    <li>Access-Control-Allow-Headers: *</li> 
 
    <li>Access-Control-Allow-Headers: x-requested-with</li> 
 
    </ul> 
 
    <p>Click a button to see if the page allows CORS</p> 
 
    <form name="form1" action="" method="get"> 
 
    <input type="button" name="btn1" value="Check Google Page" onClick="checkCorsSource('google')"> 
 
    <input type="button" name="btn1" value="Check Cors Page" onClick="checkCorsSource('cors')"> 
 
    </form> 
 
    <p id="txt1" /> 
 
</body> 
 
</html>

Cuestiones relacionadas