Une requête CORS POST fonctionne à partir de JavaScript brut, mais pourquoi pas avec jQuery?

87

J'essaie de faire une demande de publication Cross Origin, et je l'ai fait fonctionner en clair JavaScriptcomme ceci:

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

Mais j'aimerais utiliser jQuery, mais je ne peux pas le faire fonctionner. C'est ce que j'essaye:

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

Cela entraîne un échec. Si quelqu'un sait pourquoi jQuerycela ne fonctionne pas, veuillez nous le faire savoir. Merci.

(J'utilise jQuery1.5.1 et Firefox 4.0, et mon serveur répond avec un en- Access-Control-Allow-Origintête approprié )

Magmatique
la source
C'était la solution pour moi (utilisez XMLHttpRequest de Javascript) tout en faisant face à des problèmes
CORS

Réponses:

73

MISE À JOUR: Comme TimK l'a souligné, ce n'est plus nécessaire avec jquery 1.5.2. Mais si vous souhaitez ajouter des en-têtes personnalisés ou autoriser l'utilisation d'informations d'identification (nom d'utilisateur, mot de passe ou cookies, etc.), poursuivez votre lecture.


Je pense avoir trouvé la réponse! (4 heures et beaucoup de jurons plus tard)

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

Vous devez spécifier manuellement tous les en-têtes que vous accepterez (du moins c'était le cas pour moi dans FF 4.0 et Chrome 10.0.648.204).

La méthode $ .ajax de jQuery envoie l'en-tête "x-demandé-avec" pour toutes les demandes de domaine croisé (je pense que son seul domaine croisé).

L'en-tête manquant nécessaire pour répondre à la requête OPTIONS est donc:

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

Si vous passez des en-têtes non "simples", vous devrez les inclure dans votre liste (j'en envoie un de plus):

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

Donc, pour mettre tout cela ensemble, voici mon 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
la source
5
Notez que jQuery 1.5.2 a changé son comportement. Il n'ajoute plus un en-tête "X-Requested-With", donc cela pourrait ne plus être un problème. blog.jquery.com/2011/03/31/jquery-152-released (Bug 8423)
Magmatic
1
@TimK, vous avez raison! Je n'ai pas remarqué qu'ils sortaient 1.5.2. Cela étant dit, cela fonctionne également si vous en avez besoin pour un pré-vol. J'ai mis à jour ma réponse.
Will Mason
Donc, je suis confus. Vous avez quand même dû écrire un script PHP intermédiaire? Vous n'avez donc pas à vous soucier de l'utilisation d'Ajax, non? Ou est-ce que je manque quelque chose. N'existe-t-il pas de solution JavaScript uniquement?
Elisabeth
1
@Elisabeth Cette méthode ne fonctionne que si vous contrôlez la destination demandée ... ce n'est PAS un script intermédiaire. C'est le haut de notre PHP de notre emplacement demandé. Est-ce que ça fait plus de sens?
Will Mason
2
Oui! merci Will. Je pensais que vous pouviez tout contrôler du côté client, mais il semble que vous ayez besoin de contrôler les deux extrémités.
Elisabeth le
18

Une autre possibilité est que ce paramètre dataType: jsonoblige JQuery à envoyer l'en- Content-Type: application/jsontête. Ceci est considéré comme un en-tête non standard par CORS et nécessite une demande de contrôle en amont CORS. Donc, quelques choses à essayer:

1) Essayez de configurer votre serveur pour envoyer les réponses de contrôle en amont appropriées. Ce sera sous la forme d'en-têtes supplémentaires comme Access-Control-Allow-Methodset Access-Control-Allow-Headers.

2) Supprimez le dataType: jsonparamètre. JQuery devrait demander Content-Type: application/x-www-form-urlencodedpar défaut, mais pour être sûr, vous pouvez le remplacer dataType: jsonparcontentType: 'application/x-www-form-urlencoded'

Monsur
la source
Merci pour les idées. J'ai essayé de ne pas définir dataType, et de le définir comme application/x-www-form-urlencodedet même text/plain. Et j'ai essayé d'ajouter un en-tête de réponse de Access-Control-Allow-Methods "POST, GET, OPTIONS"Rien n'a fonctionné.
Magmatic
Pouvez-vous regarder dans la console d'erreur JavaScript (ou la console Firebug) et voir s'il y a des erreurs lors de la demande? De plus, si vous savez utiliser Wireshark, vous pouvez l'utiliser pour voir les requêtes HTTP réelles passant par le câble.
monsur
1
"Une autre possibilité est que la définition de dataType: json oblige JQuery à envoyer l'en-tête Content-Type: application / json" - Cela ne se produit pas. dataTypeinfluence l'en- Accepttête de la demande mais pas l'en- Content-Typetête de la demande.
Quentin
9

Vous envoyez des "paramètres" en js: request.send(params);

mais "data" dans jquery ". Les données sont-elles définies?: data:data,

De plus, vous avez une erreur dans l'URL:

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

Vous mélangez la syntaxe avec celle de $ .post


Mise à jour : je cherchais sur Google sur la base de la réponse de monsur, et j'ai trouvé que vous devez ajouter Access-Control-Allow-Headers: Content-Type(ci-dessous est le paragraphe complet)

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

Comment CORS fonctionne

CORS fonctionne de manière très similaire au fichier crossdomain.xml de Flash. Fondamentalement, le navigateur enverra une demande interdomaine à un service, en définissant l'en-tête HTTP Origin sur le serveur demandeur. Le service comprend quelques en-têtes comme Access-Control-Allow-Origin pour indiquer si une telle demande est autorisée.

Pour les gestionnaires de connexions BOSH, il suffit de spécifier que toutes les origines sont autorisées, en définissant la valeur de Access-Control-Allow-Origin sur *. L'en-tête Content-Type doit également figurer sur la liste blanche dans l'en-tête Access-Control-Allow-Headers.

Enfin, pour certains types de demandes, y compris les demandes du gestionnaire de connexions BOSH, la vérification des autorisations sera pré-envoyée. Le navigateur effectuera une requête OPTIONS et s'attendra à récupérer des en-têtes HTTP indiquant les origines autorisées, les méthodes autorisées et la durée de cette autorisation. Par exemple, voici ce que les patchs Punjab et ejabberd que j'ai retournés pour OPTIONS:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type 
Access-Control-Max-Age: 86400
Aleadam
la source
1
Pardon. Oui. var data = {action:"something"}
Magmatic le
Vous pouvez comparer la syntaxe des deux fonctions ici: api.jquery.com/jQuery.post
Aleadam
Je viens de l'essayer avec l'url dans les paramètres, mais même problème. La fonction .ajax peut le prendre dans les deux sens.
Magmatic le
J'avais déjà deux de ces en-têtes. J'ai ajouté les deux autres. Toujours "échec" avec jQuery. Le javascript simple fonctionne toujours.
Magmatic le
La dernière chose à laquelle je peux penser est d'utiliser api.jquery.com/jQuery.ajaxSetup pour définir jQuery.ajaxSetup({'beforeSend': function(xhr) {xhr.setRequestHeader(string, string)}})et jouer avec les différents en-têtes envoyés (un exemple pour les rails ici: railscasts.com/episodes/136-jquery )
Aleadam
1

Cors change la méthode de demande avant que cela ne soit fait, de POST à ​​OPTIONS, donc vos données de publication ne seront pas envoyées. La manière qui a fonctionné pour gérer ce problème de cors consiste à exécuter la requête avec ajax, qui ne prend pas en charge la méthode OPTIONS. code d'exemple:

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

avec ces en-têtes sur le serveur 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
la source
-2

Modifiez votre Jquery de la manière suivante:

$.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
la source
Pourquoi voudrais-je rendre mes callas Ajax synchrones!?
Radko Dinev
contentType: 'application/json', data: JSONObject,- Le serveur n'attend pas de JSON, donc l'envoi de JSON n'aurait pas de sens. Aussi Il n'y a pas une telle chose comme un objet JSON .
Quentin
1
headers: { 'Access-Control-Allow-Origin': '*' }, //add this line- Ne fais jamais ça. Access-Control-Allow-Originest un en- tête de réponse , pas un en-tête de demande. Au mieux, cela ne fera rien. Au pire, il convertira la demande d'une simple demande en une demande de contrôle en amont, ce qui la rend toujours plus difficile à traiter sur le serveur.
Quentin