web-dev-qa-db-fra.com

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

Avoir 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 à un <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_TYPE sur 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 dans Firefox, et comment puis-je le résoudre pour Firefox?

 En-têtes de réponse 
 Date: mercredi, 08 juillet 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'attente Keep-Alive = 15, max = 100 
 Connexion Keep-Alive 
 Content-Type text/html 
 
 En-têtes de demande 
 Formulaire de commande d'hôte: 8888 
 User-Agent Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5 ; en-US; va: 1.9.1) Gecko/20090624 Firefox/3.5 
 Acceptez text/html, application/xhtml + xml, application/xml; q = 0,9, */*; q = 0,8 
 Accept-Language fr-us, en; q = 0.5 
 Accepter-coder gzip, deflate 
 Accepter-Jeu de caractères ISO-8859-1, utf-8; q = 0,7, *; q = 0,7 
 Keep-Alive 300 
 Connexion de maintien en vie 
 Origine http://ux.inetu.act.org 
 Méthode de demande de contrôle d'accès POST 
 En-têtes de demande de contrôle d'accès x-demandé-avec 

Voici une image de la sortie de Firebug:

327
fitzgeraldsteele

La raison de l'erreur est la même stratégie d'origine. Cela vous permet seulement de faire XMLHTTPRequests dans votre propre domaine. Voyez si vous pouvez utiliser un callback JSONP :

$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );
168
Jonas Skovmand

J'ai utilisé le code suivant du côté Django pour interpréter la requête OPTIONS et définir les en-têtes de contrôle d'accès requis. Après cela, mes requêtes multi-domaines de Firefox ont commencé à fonctionner. Comme dit précédemment, le navigateur envoie d’abord la demande OPTIONS puis immédiatement après que le message 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":
        # ... 

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

56
Juha Palomäki

Cet article du centre des développeurs de mozilla décrit divers scénarios de requête inter-domaines. L'article semble indiquer qu'une demande POST avec le type de contenu "application/x-www-form-urlencoded" doit être envoyée en tant que "demande simple" (sans demande OPTION de "contrôle en amont"). J'ai toutefois constaté que Firefox avait envoyé la demande OPTIONS, même si mon POST avait été envoyé avec ce type de contenu.

J'ai pu réaliser cela en créant un gestionnaire de requêtes d'options sur le serveur, définissant l'en-tête de réponse 'Access-Control-Allow-Origin-Origin' sur '*'. Vous pouvez être plus restrictif en lui attribuant une valeur spécifique, telle que ' http://someurl.com '. En outre, j'ai lu que, supposément, vous pouvez spécifier une liste d'origines multiples séparée par des virgules, mais je ne pouvais pas que cela fonctionne.

Une fois que Firefox a reçu la réponse à la demande OPTIONS avec une valeur acceptable "Accès-Contrôle-Autoriser-Origine", il envoie la demande POST.

16
Mike C

J'ai résolu ce problème en utilisant une solution entièrement basée sur Apache. Dans mon vhost/htaccess je mets 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’avez peut-être pas besoin de cette dernière partie, en fonction de ce qui se passe lorsqu’Apache exécute votre script cible. Le mérite revient au folk ServerFault amical pour la dernière partie.

15
Mark McDonald

Ce PHP en haut du script de réponse 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);
}
10
Chad Clark

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

7
Slav

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

Pour les méthodes de requête HTTP pouvant entraîner des effets secondaires sur les données utilisateur (en particulier pour les méthodes HTTP autres que GET ou pour l'utilisation de POST avec certains types MIME), la spécification impose aux navigateurs de "contrôler en amont" la demande. , sollicitant des méthodes prises en charge auprès du serveur avec une méthode de requête HTTP OPTIONS, puis, après "approbation" du serveur, envoyant la requête réelle avec la méthode de requête HTTP réelle.

La spécification Web fait 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;
                }
    }
6
thinkhy

Je cherchais dans la source 1.3.2, lorsque JSONP est utilisé, la requête est créée en créant un élément SCRIPT de manière dynamique, qui dépasse les règles de navigation des navigateurs. Naturellement, vous ne pouvez pas faire de requête POST à l'aide d'un élément SCRIPT, le navigateur extraira le résultat à l'aide de GET.

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

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

4
Slava0008

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'un jQuery $.post pour obtenir du contenu HTML dû à PageHandlerFactory étant limité à une réponse GET,HEAD,POST,DEBUG Verbs. 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 IIS Manager, en sélectionnant votre site Web, puis en sélectionnant Mappages de gestionnaires, puis double-cliquez dans votre PageHandlerFactory pour les fichiers * .apx selon vos besoins (Nous utilisons un pool d'applications intégré avec Framework 4.0). Cliquez sur Demander des restrictions, puis allez à l'onglet Verbs et appliquez votre modification.

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

3
fboiton

Il me semble que si o.url = 'index.php' et que ce fichier existe est ok et renvoie un message de réussite dans la console. Il retourne une erreur si j'utilise 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.

2
Elzo Valugi

Vérifiez si l'URL action de votre formulaire inclut la partie www du domaine, tandis que la page d'origine que vous avez ouverte est affichée sans www.

Généralement fait pour les URL canoniques.

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

2
Bijay Rungta

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

1
Niehztog

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

Comment envoyer une demande POST entre domaines à l'aide de JavaScript?

1
rynop

La solution à ceci est:

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

cela a fonctionné en appelant l'API Facebook et avec Firefox. Firebug utilise GET au lieu de OPTIONS avec les conditions ci-dessus (les deux).

1
Antonio Gulli

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 juste text/plain ... pas le reste des paramètres mentionnés à mozilla ici

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

FYI: Le document de Moz mentionné ci-dessus 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.

0
Drew

Vous devez travailler sur le 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 la même chose dans le script PHP et cela fonctionnera. Simplement: à la première requête, le navigateur demande au serveur s’il est autorisé à envoyer de telles données avec ce type et la seconde demande est correcte/autorisée.

0
Fanda

Essayez d'ajouter ce qui suit:

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

J'ai déjà ce code manipulant bien ma situation de 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 très bien localement et à distance, mais pas pour les téléchargements à distance.

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

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

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

Maintenant, en demandant OPTIONS, il n'enverra que l'en-tête et le résultat.

0
Ratata Tata

Pouvez-vous essayer ceci sans

contentType:application/x-www-form-urlencoded

0
Mathias F

Essayez d'ajouter l'option:

dataType: "json"

0
ScottE

J'ai utilisé une URL de proxy pour résoudre un problème similaire lorsque je souhaite publier des données sur mon serveur Apache hébergé sur un autre serveur. (Cela peut ne pas être la réponse parfaite mais cela résout mon problème.)

Suivez cette URL: tilisation de 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 poster des données sur/solr au lieu de poster des données sur http: // ip: 8983/solr / *. Ensuite, il affichera des données dans la même origine.

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

     }
});         
}
0
Naser Gulzade