Una solicitud CORS POST funciona desde JavaScript simple, pero ¿por qué no con jQuery?

88

Estoy tratando de hacer una solicitud de publicación de Cross Origin, y lo hice funcionar en forma simple JavaScriptasí:

var request = new XMLHttpRequest();
var params = "action=something";
request.open('POST', url, true);
request.onreadystatechange = function() {if (request.readyState==4) alert("It worked!");};
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Content-length", params.length);
request.setRequestHeader("Connection", "close");
request.send(params);

Pero me gustaría usar jQuery, pero no puedo hacer que funcione. Esto es lo que estoy intentando:

$.ajax(url, {
    type:"POST",
    dataType:"json",
    data:{action:"something"}, 
    success:function(data, textStatus, jqXHR) {alert("success");},
    error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Esto resulta en una falla. Si alguien sabe por qué jQueryno funciona, háganoslo saber. Gracias.

(Estoy usando jQuery1.5.1 y Firefox 4.0, y mi servidor responde con un Access-Control-Allow-Originencabezado adecuado )

Magmático
fuente
Esta fue la solución para mí (use XMLHttpRequest de Javascript) mientras enfrentaba problemas de CORS con Ionic framework 3.
jeudyx

Respuestas:

73

ACTUALIZACIÓN: Como señaló TimK, esto ya no es necesario con jquery 1.5.2. Pero si desea agregar encabezados personalizados o permitir el uso de credenciales (nombre de usuario, contraseña o cookies, etc.), siga leyendo.


¡Creo que encontré la respuesta! (4 horas y muchas maldiciones después)

//This does not work!!
Access-Control-Allow-Headers: *

Debe especificar manualmente todos los encabezados que aceptará (al menos ese fue mi caso en FF 4.0 y Chrome 10.0.648.204).

El método $ .ajax de jQuery envía el encabezado "x-required-with" para todas las solicitudes de dominio cruzado (creo que es el único dominio cruzado).

Entonces, el encabezado faltante necesario para responder a la solicitud de OPCIONES es:

//no longer needed as of jquery 1.5.2
Access-Control-Allow-Headers: x-requested-with

Si está pasando encabezados que no sean "simples", deberá incluirlos en su lista (le envío uno más):

//only need part of this for my custom header
Access-Control-Allow-Headers: x-requested-with, x-requested-by

Entonces, para ponerlo todo junto, aquí está mi PHP:

// * wont work in FF w/ Allow-Credentials
//if you dont need Allow-Credentials, * seems to work
header('Access-Control-Allow-Origin: http://www.example.com');
//if you need cookies or login etc
header('Access-Control-Allow-Credentials: true');
if ($this->getRequestMethod() == 'OPTIONS')
{
  header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
  header('Access-Control-Max-Age: 604800');
  //if you need special headers
  header('Access-Control-Allow-Headers: x-requested-with');
  exit(0);
}
Will Mason
fuente
5
Tenga en cuenta que jQuery 1.5.2 ha cambiado su comportamiento. Ya no agrega un encabezado "X-Requested-With", por lo que es posible que esto ya no sea un problema. blog.jquery.com/2011/03/31/jquery-152-released (Error 8423)
Magmatic
1
@TimK, ¡tienes razón! No me di cuenta de que lanzaron 1.5.2. Dicho esto, esto también funciona si necesita que se vuele previamente. Actualicé mi respuesta.
Will Mason
Entonces, estoy confundido. ¿Terminó teniendo que escribir un script PHP intermedio de todos modos? Entonces, no necesita preocuparse por usar Ajax, ¿verdad? O me estoy perdiendo algo. ¿No existe una solución única de JavaScript?
Elisabeth
1
@Elisabeth Este método solo funciona si controlas el destino solicitado ... NO es un script intermedio. Es la parte superior de nuestro PHP de nuestra ubicación solicitada. ¿Eso tiene más sentido?
Will Mason
2
¡Si! gracias Will. Pensé que podías controlar todo desde el lado del cliente, pero parece que necesitas el control de ambos extremos.
Elisabeth
18

Otra posibilidad es que la configuración dataType: jsonhaga que JQuery envíe el Content-Type: application/jsonencabezado. Esto se considera un encabezado no estándar por CORS y requiere una solicitud de verificación previa de CORS. Así que algunas cosas para probar:

1) Intente configurar su servidor para enviar las respuestas de verificación previa adecuadas. Esto tendrá la forma de encabezados adicionales como Access-Control-Allow-Methodsy Access-Control-Allow-Headers.

2) Elimine la dataType: jsonconfiguración. JQuery debería solicitar Content-Type: application/x-www-form-urlencodedde forma predeterminada, pero solo para estar seguro, puede reemplazar dataType: jsonconcontentType: 'application/x-www-form-urlencoded'

monsur
fuente
Gracias por las ideas. Intenté no configurar dataType y configurarlo para que sea application/x-www-form-urlencodede incluso text/plain. E intenté agregar un encabezado de respuesta de Access-Control-Allow-Methods "POST, GET, OPTIONS"Nada funcionó.
Magmatic
¿Puede buscar en la consola de errores de JavaScript (o la consola de Firebug) y ver si hay algún error durante la solicitud? Además, si sabe cómo usar Wireshark, puede usarlo para ver las solicitudes HTTP reales que pasan por el cable.
monsur
1
"Otra posibilidad es que la configuración de dataType: json haga que JQuery envíe el encabezado Content-Type: application / json". Esto no sucede. dataTypeinfluye en el Acceptencabezado de la solicitud, pero no en el Content-Typeencabezado de la solicitud.
Quentin
9

Estás enviando "params" en js: request.send(params);

pero "datos" en jquery ". ¿Los datos están definidos ?: data:data,

Además, tiene un error en la URL:

$.ajax( {url:url,
         type:"POST",
         dataType:"json",
         data:data, 
         success:function(data, textStatus, jqXHR) {alert("success");},
         error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Estás mezclando la sintaxis con la de $ .post


Actualización : estaba buscando en Google según la respuesta de monsur, y descubrí que necesita agregar Access-Control-Allow-Headers: Content-Type(a continuación se muestra el párrafo completo)

http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/

Cómo funciona CORS

CORS funciona de manera muy similar al archivo crossdomain.xml de Flash. Básicamente, el navegador enviará una solicitud entre dominios a un servicio, configurando el origen del encabezado HTTP en el servidor solicitante. El servicio incluye algunos encabezados como Access-Control-Allow-Origin para indicar si dicha solicitud está permitida.

Para los administradores de conexión BOSH, es suficiente especificar que todos los orígenes están permitidos, estableciendo el valor de Access-Control-Allow-Origin en *. El encabezado Content-Type también debe figurar en la lista blanca en el encabezado Access-Control-Allow-Headers.

Finalmente, para ciertos tipos de solicitudes, incluidas las solicitudes del administrador de conexiones BOSH, la verificación de permisos se realizará previamente. El navegador realizará una solicitud de OPCIONES y esperará recuperar algunos encabezados HTTP que indican qué orígenes están permitidos, qué métodos están permitidos y cuánto tiempo durará esta autorización. Por ejemplo, esto es lo que los parches de Punjab y ejabberd devolví para OPCIONES:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type 
Access-Control-Max-Age: 86400
Aleadam
fuente
1
Lo siento. Si. var data = {action:"something"}
Magmatic
Puede comparar la sintaxis de ambas funciones aquí: api.jquery.com/jQuery.post
Aleadam
Lo intenté con la URL en la configuración, pero el mismo problema. La función .ajax puede tomarlo de cualquier manera.
Magmatic
Ya tenía dos de esos encabezados. Agregué los otros dos. Todavía "fracaso" con jQuery. El javascript simple todavía funciona.
Magmatic
Lo último que se me ocurre es usar api.jquery.com/jQuery.ajaxSetup para configurar jQuery.ajaxSetup({'beforeSend': function(xhr) {xhr.setRequestHeader(string, string)}})y jugar con los diferentes encabezados enviados (un ejemplo de rieles aquí: railscasts.com/episodios/136-jquery )
Aleadam
1

Cors cambia el método de solicitud antes de que se haga, de POST a OPTIONS, por lo que no se enviarán los datos de tu publicación. La forma en que funcionó para manejar este problema de cors es realizar la solicitud con ajax, que no admite el método OPTIONS. código de ejemplo:

        $.ajax({
            type: "POST",
            crossdomain: true,
            url: "http://localhost:1415/anything",
            dataType: "json",
            data: JSON.stringify({
                anydata1: "any1",
                anydata2: "any2",
            }),
            success: function (result) {
                console.log(result)
            },
            error: function (xhr, status, err) {
                console.error(xhr, status, err);
            }
        });

con estos encabezados en el servidor c #:

                    if (request.HttpMethod == "OPTIONS")
                    {
                          response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
                          response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
                          response.AddHeader("Access-Control-Max-Age", "1728000");
                    }
                    response.AppendHeader("Access-Control-Allow-Origin", "*");
Lucas silva de souza sandim
fuente
-2

Modifique su Jquery de la siguiente manera:

$.ajax({
            url: someurl,
            contentType: 'application/json',
            data: JSONObject,
            headers: { 'Access-Control-Allow-Origin': '*' }, //add this line
            dataType: 'json',
            type: 'POST',                
            success: function (Data) {....}
});
Soma Sarkar
fuente
¿Por qué querría hacer que mis callas Ajax sean sincrónicas?
Radko Dinev
contentType: 'application/json', data: JSONObject,- El servidor no espera JSON, por lo que enviar JSON no tendría sentido. Además, no existe un objeto JSON .
Quentin
1
headers: { 'Access-Control-Allow-Origin': '*' }, //add this line- Nunca hagas eso. Access-Control-Allow-Origines un encabezado de respuesta , no un encabezado de solicitud. En el mejor de los casos, esto no hará nada. En el peor de los casos, convertirá la solicitud de una solicitud simple a una solicitud de verificación previa, lo que hace que sea cada vez más difícil de tratar en el servidor.
Quentin