jQuery $ .ajax (), $ .post envoyant des «OPTIONS» en tant que REQUEST_METHOD dans Firefox

330

Ayant des problèmes avec ce que je pensais être un plugin jQuery relativement simple ...

Le plugin devrait récupérer les données d'un script php via ajax pour ajouter des options à a <select>. La requête ajax est assez générique:

$.ajax({
  url: o.url,
  type: 'post',
  contentType: "application/x-www-form-urlencoded",
  data: '{"method":"getStates", "program":"EXPLORE"}',
  success: function (data, status) {
    console.log("Success!!");
    console.log(data);
    console.log(status);
  },
  error: function (xhr, desc, err) {
    console.log(xhr);
    console.log("Desc: " + desc + "\nErr:" + err);
  }
});

Cela semble bien fonctionner dans Safari. Dans Firefox 3.5, le REQUEST_TYPEsur le serveur est toujours «OPTIONS» et les données $ _POST n'apparaissent pas. Apache enregistre la demande sous le type 'OPTIONS':

::1 - - [08/Jul/2009:11:43:27 -0500] "OPTIONS sitecodes.php HTTP/1.1" 200 46

Pourquoi cet appel ajax fonctionnerait-il dans Safari, mais pas Firefox, et comment le corriger pour Firefox?

En-têtes de réponse
Date: mer., 08 juil. 2009 21:22:17 GMT
Serveur: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Contenu-Longueur 46
Délai d'expiration Keep-Alive = 15, max = 100
Connexion persistante
Type de contenu texte / html

En-têtes de demande
Formulaire de commande de l'hôte: 8888
User-Agent Mozilla / 5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv: 1.9.1) Gecko / 20090624 Firefox / 3.5
Accepter texte / html, application / xhtml + xml, application / xml; q = 0,9, * / *; q = 0,8
Accept-Language en-us, en; q = 0,5
Accepter-encoder gzip, dégonfler
Accept-Charset ISO-8859-1, utf-8; q = 0,7, *; q = 0,7
Keep-Alive 300
Connexion en vie
Origine http://ux.inetu.act.org
POST-Control-Request-Method
En-têtes de demande de contrôle d'accès x demandés avec

Voici une image de la sortie Firebug:

fitzgeraldsteele
la source
Pouvez-vous publier la réponse de Firebug et demander des en-têtes. Je ne reçois aucune erreur lorsque j'exécute un code similaire dans Firefox.
MitMaro
Ajout d'informations d'en-tête et d'une image de Firebug.
fitzgeraldsteele
Je viens d'avoir ce même problème lors de la mise en œuvre d'un serveur Web intégré. Merci d'avoir demandé :)
Robert Gould
Si vous recherchez des solutions Java JAX-RS, voir ici: Access-Control-Allow-Origin
Tobias Sarnow
Le comportement de Firefox semble avoir changé maintenant? Je ne reçois aucune demande d'option.
Buge

Réponses:

169

La raison de l'erreur est la même politique d'origine. Il vous permet uniquement d'effectuer des requêtes XMLHTTPRequests sur votre propre domaine. Voyez si vous pouvez utiliser un rappel JSONP à la place:

$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );
Jonas Skovmand
la source
26
pourquoi Firefox est-il le seul navigateur à le faire? Je veux un article et non un get.
Maslow
11
Crossite-POST: Quelqu'un connaît-il une solution pour faire un POST avec application / json comme Content-Type?
schoetbi
13
Alors, quelle est exactement la solution?
Nik So
3
La recherche d'une solution à cela aussi et l'utilisation de getJSON au lieu de l'appel ajax ne le font pas pour moi car c'est beaucoup plus limité.
Timo Wallenius
1
@schoetbi pour cela, vous allez avoir besoin d'utiliser CORS, qui est bien pris en charge dans les nouveaux navigateurs ... prise en charge limitée dans IE8-9, et a besoin d'une prise en charge côté serveur.
Tracker1
57

J'ai utilisé le code suivant du côté de Django pour interpréter la demande OPTIONS et pour définir les en-têtes de contrôle d'accès requis. Après cela, mes demandes inter-domaines de Firefox ont commencé à fonctionner. Comme dit précédemment, le navigateur envoie d'abord la requête OPTIONS puis immédiatement après cela le POST / GET

def send_data(request):
    if request.method == "OPTIONS": 
        response = HttpResponse()
        response['Access-Control-Allow-Origin'] = '*'
        response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response['Access-Control-Max-Age'] = 1000
        # note that '*' is not valid for Access-Control-Allow-Headers
        response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
        return response
    if request.method == "POST":
        # ... 

Modifier: il semble qu'au moins dans certains cas, vous devez également ajouter les mêmes en-têtes de contrôle d'accès à la réponse réelle. Cela peut être un peu déroutant, car la demande semble réussir, mais Firefox ne transmet pas le contenu de la réponse au Javascript.

Juha Palomäki
la source
Votre modification concernant la réponse POST / GET réelle est un peu effrayante; si quelqu'un peut le confirmer, merci de nous le faire savoir ici!
Arjan
Je ne sais pas s'il s'agit d'un bug ou d'une fonctionnalité, mais il semble que quelqu'un d'autre l'ait également remarqué. Voir par exemple kodemaniak.de/?p=62 et rechercher "corps de réponse vide"
Juha Palomäki
2
Il existe une différence entre les demandes simples et celles qui nécessitent un contrôle en amont. Votre "solution" ne fonctionnera qu'avec les demandes de contrôle en amont, ce n'est donc pas une vraie solution. Chaque fois que vous obtenez un en-tête "Origin:" dans les en-têtes de demande, vous devez répondre en l'autorisant.
odinho - Velmont
1
Je pense que l'en-tête Access-Control-Allow-Headersdoit contenir la valeur x-csrf-token, non x-csrftoken.
JellicleCat
16

Cet article du centre de développement mozilla décrit divers scénarios de demande interdomaine. L'article semble indiquer qu'une demande POST avec le type de contenu «application / x-www-form-urlencoded» doit être envoyée comme une «demande simple» (sans demande OPTIONS de «contrôle en amont»). J'ai trouvé, cependant, que Firefox a envoyé la demande OPTIONS, même si mon POST a été envoyé avec ce type de contenu.

J'ai pu faire ce travail en créant un gestionnaire de demande d'options sur le serveur, qui a défini l'en-tête de réponse «Access-Control-Allow-Origin» sur «*». Vous pouvez être plus restrictif en le définissant sur quelque chose de spécifique, comme « http://someurl.com ». De plus, j'ai lu que, soi-disant, vous pouvez spécifier une liste séparée par des virgules de plusieurs origines, mais je n'ai pas pu faire fonctionner cela.

Une fois que Firefox reçoit la réponse à la demande OPTIONS avec une valeur acceptable de «contrôle d'accès-autorisation-origine», il envoie la demande POST.

Mike C
la source
15

J'ai résolu ce problème en utilisant une solution entièrement basée sur Apache. Dans mon vhost / htaccess j'ai mis le bloc suivant:

# enable cross domain access control
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"

# force apache to return 200 without executing my scripts
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]

Vous n'aurez peut-être pas besoin de cette dernière partie, selon ce qui se passe lorsque Apache exécute votre script cible. Le mérite revient au sympathique ServerFault pour cette dernière partie.

Mark McDonald
la source
Votre réponse m'a aidé, mais si vous avez besoin de logique derrière CORS, cela ne résout pas complètement.
Ratata Tata
10

Ce PHP en haut du script répondant semble fonctionner. (Avec Firefox 3.6.11. Je n'ai pas encore fait beaucoup de tests.)

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) {
    header('Access-Control-Allow-Headers: '
           . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
} else {
    header('Access-Control-Allow-Headers: *');
}

if("OPTIONS" == $_SERVER['REQUEST_METHOD']) {
    exit(0);
}
Chad Clark
la source
Cela pourrait être une question de goût, mais toujours envoyer les en- têtes de réponse (aussi GET, POST...) est un peu trop à mon goût. (Et, je me demande si toujours envoyer ceux-ci est conforme aux spécifications?)
Arjan
3
envelopper dans if ($ _ SERVER ['HTTP_ORIGIN']). Si cet en-tête est là, c'est une demande CORS, sinon, pas besoin d'envoyer quoi que ce soit.
odinho - Velmont
7

J'ai eu le même problème avec l'envoi de demandes à Google Maps, et la solution est assez simple avec jQuery 1.5 - pour une utilisation dataType dataType: "jsonp"

slave
la source
12
Incompatible avec la méthode POST.
Pavel Vlasov
1
Cela fonctionne avec une méthode GET mais c'est une solution très limitée. Par exemple, ce faisant, vous ne pouvez pas renvoyer une réponse avec un en-tête spécifique comprenant un jeton.
svassr
6

Le coupable est une demande de contrôle en amont utilisant la méthode OPTIONS

Pour les méthodes de requête HTTP qui peuvent provoquer des effets secondaires sur les données utilisateur (en particulier, pour les méthodes HTTP autres que GET, ou pour une utilisation POST avec certains types MIME), la spécification impose aux navigateurs de "contrôler en amont" la requête, en sollicitant les méthodes prises en charge à partir du serveur avec une méthode de demande HTTP OPTIONS, puis, sur "approbation" du serveur, envoi de la demande réelle avec la méthode de demande HTTP réelle.

Les spécifications Web font référence à: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

J'ai résolu le problème en ajoutant les lignes suivantes dans Nginx conf.

    location / {
               if ($request_method = OPTIONS ) {
                   add_header Access-Control-Allow-Origin  "*";
                   add_header Access-Control-Allow-Methods "POST, GET, PUT, UPDATE, DELETE, OPTIONS";
                   add_header Access-Control-Allow-Headers "Authorization";
                   add_header Access-Control-Allow-Credentials  "true";
                   add_header Content-Length 0;
                   add_header Content-Type text/plain;
                   return 200;
               }
    location ~ ^/(xxxx)$ {
                if ($request_method = OPTIONS) {
                    rewrite ^(.*)$ / last;
                }
    }
pense
la source
1
Cette réponse est très utile merci. Le fait que le navigateur envoie une demande de contrôle en amont avec une méthode OPTIONS n'est pas évident.
Normangorman
4

Je regardais la source 1.3.2, lors de l'utilisation de JSONP, la demande est faite en construisant un élément SCRIPT dynamiquement, qui dépasse la politique du même domaine du navigateur. Naturellement, vous ne pouvez pas faire de requête POST à ​​l'aide d'un élément SCRIPT, le navigateur récupère le résultat à l'aide de GET.

Comme vous demandez un appel JSONP, l'élément SCRIPT n'est pas généré, car il ne le fait que lorsque le type d'appel AJAX est défini sur GET.

http://dev.jquery.com/ticket/4690


la source
4

Nous avons eu un problème comme celui-ci avec ASP.Net. Notre IIS renvoyait une erreur de serveur interne lors de la tentative d'exécution d'une jQuery $.postpour obtenir du contenu html en raison de PageHandlerFactory était limité pour répondre uniquement aux GET,HEAD,POST,DEBUGverbes. Vous pouvez donc modifier cette restriction en ajoutant le verbe "OPTIONS" à la liste ou en sélectionnant "Tous les verbes"

Vous pouvez modifier cela dans votre gestionnaire IIS, en sélectionnant votre site Web, puis en sélectionnant Mappages de gestionnaires, double-cliquez dans votre PageHandlerFactory pour les fichiers * .apx selon vos besoins (nous utilisons le pool d'applications intégré avec le framework 4.0). Cliquez sur Request Restrictions, puis allez dans Verbs Tabn et appliquez votre modification.

Maintenant, notre $.postdemande fonctionne comme prévu :)

fboiton
la source
2

Vérifiez si l' actionURL de votre formulaire comprend la wwwpartie du domaine, tandis que la page d'origine que vous avez ouverte est affichée sans www.

Généralement effectué pour les URL canoniques.

J'ai lutté pendant des heures avant de tomber sur cet article et j'ai trouvé le soupçon de Cross Domain.

Bijay Rungta
la source
2

Il semble que si o.url = 'index.php'et que ce fichier existe est correct et renvoie un message de réussite dans la console. Il renvoie une erreur si j'utilise l'URL:http://www.google.com

Si vous faites une demande de publication, pourquoi ne pas utiliser directement la méthode $ .post :

$.post("test.php", { func: "getNameAndTime" },
    function(data){
        alert(data.name); // John
        console.log(data.time); //  2pm
    }, "json");

C'est tellement plus simple.

Elzo Valugi
la source
J'ai la même chose avec ça ... pensais que je devrais utiliser $ .ajax () afin que je puisse au moins obtenir des informations de débogage sur la condition d'erreur ..
fitzgeraldsteele
1

J'ai publié un exemple clair de la façon de résoudre ce problème si vous contrôlez le code serveur du domaine vers lequel vous POSTEZ. Cette réponse est abordée dans ce fil, mais cela explique plus clairement l'OMI.

Comment envoyer une demande POST interdomaine via JavaScript?

rynop
la source
1

La solution à cela est:

  1. utiliser dataType: json
  2. ajouter &callback=?à votre URL

cela a fonctionné en appelant Facebook API et avec Firefox. Firebug utilise à la GETplace des OPTIONSconditions ci-dessus (les deux).

Antonio Gulli
la source
1

Une autre possibilité pour contourner le problème est d'utiliser un script proxy. Cette méthode est décrite par exemple ici

Niehztog
la source
0

Pouvez-vous essayer cela sans

contentType:application/x-www-form-urlencoded

Mathias F
la source
Même résultat, j'ai peur.
fitzgeraldsteele
0

Essayez d'ajouter l'option:

dataType: "json"

ScottE
la source
2
cela a fonctionné, pourquoi json est considéré comme "sûr" pour les demandes inter-domaines?
Nik So
0
 function test_success(page,name,id,divname,str)
{ 
 var dropdownIndex = document.getElementById(name).selectedIndex;
 var dropdownValue = document.getElementById(name)[dropdownIndex].value;
 var params='&'+id+'='+dropdownValue+'&'+str;
 //makerequest_sp(url, params, divid1);

 $.ajax({
    url: page,
    type: "post",
    data: params,
    // callback handler that will be called on success
    success: function(response, textStatus, jqXHR){
        // log a message to the console
        document.getElementById(divname).innerHTML = response;

        var retname = 'n_district';
        var dropdownIndex = document.getElementById(retname).selectedIndex;
        var dropdownValue = document.getElementById(retname)[dropdownIndex].value;
        if(dropdownValue >0)
        {
            //alert(dropdownValue);
            document.getElementById('inputname').value = dropdownValue;
        }
        else
        {
            document.getElementById('inputname').value = "00";
        }
        return;
        url2=page2; 
        var params2 = parrams2+'&';
        makerequest_sp(url2, params2, divid2);

     }
});         
}
Naser Gulzade
la source
La question a déjà été répondue il y a 6 mois. Comment cela résout-il cela?
Barmar
0

J'ai eu un problème similaire en essayant d'utiliser l'API Facebook.

Le seul contentType qui n'a pas envoyé la demande de contrôle en amont semblait être simplement du texte / simple ... pas le reste des paramètres mentionnés chez mozilla ici

  • Pourquoi est-ce le seul navigateur qui fait ça?
  • Pourquoi Facebook ne connaît-il pas et n'accepte-t-il pas la demande de contrôle en amont?

FYI: Le document Moz susmentionné suggère que les en-têtes X-Lori devraient déclencher une demande de contrôle en amont ... ce n'est pas le cas.

A dessiné
la source
0

Vous devez faire un peu de travail côté serveur. Je vois que vous utilisez PHP côté serveur, mais la solution pour l'application Web .NET est ici: Impossible de définir le type de contenu sur 'application / json' dans jQuery.ajax

Faites de même dans le script PHP et cela fonctionnera. Simplement: à la première demande, le navigateur demande au serveur s'il est autorisé à envoyer de telles données avec un tel type et la deuxième demande est la bonne / autorisée.

Fanda
la source
0

Essayez d'ajouter ce qui suit:

dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),  
Mary Jain
la source
0

J'ai utilisé une URL de proxy pour résoudre un problème similaire lorsque je veux publier des données sur mon apache solr hébergé sur un autre serveur. (Ce n'est peut-être pas la réponse parfaite, mais cela résout mon problème.)

Suivez cette URL: En utilisant Mode-Rewrite pour le proxy , j'ajoute cette ligne à mon httpd.conf:

 RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P]

Par conséquent, je peux simplement publier des données dans / solr au lieu de publier des données dans http: // ip: 8983 / solr / *. Ensuite, il affichera les données dans la même origine.

Bernice
la source
0

J'ai déjà ce code qui gère bien ma situation cors en php:

header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Credentials: true' );

Et cela fonctionnait bien localement et à distance, mais pas pour les téléchargements à distance.

Quelque chose se passe avec apache / php OU mon code, je n'ai pas pris la peine de le rechercher, lorsque vous demandez des OPTIONS, il renvoie mon en-tête avec les règles cors mais avec le résultat 302. Par conséquent, mon navigateur ne reconnaît pas une situation acceptable.

Ce que j'ai fait, basé sur la réponse de @Mark McDonald, c'est de mettre ce code après mon en-tête:

if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' )
{
    header("HTTP/1.1 202 Accepted");
    exit;
}

Maintenant, lors de la demande, OPTIONSil suffit d'envoyer l'en-tête et le résultat 202.

Ratata Tata
la source
-1

Veuillez noter:

JSONP ne prend en charge que la méthode de demande GET.

* Envoyer une demande par Firefox : *

$.ajax({
   type: 'POST',//<<===
   contentType: 'application/json',
   url: url,
   dataType: "json"//<<=============
    ...
});

Au-dessus de la demande, envoyez par OPTIONS (tandis que ==> tapez: 'POST' ) !!!!

$.ajax({
    type: 'POST',//<<===
    contentType: 'application/json',
    url: url,
    dataType: "jsonp"//<<==============
    ...
});

Mais au-dessus de la demande envoyée par GET (while ==> type: 'POST' ) !!!!

Lorsque vous êtes en "communication inter-domaines", faites attention et soyez prudent.

M.Namjo
la source