AJAX dans Chrome envoie des OPTIONS au lieu de GET / POST / PUT / DELETE?

107

Je travaille sur une application web interne au travail. Dans IE10, les requêtes fonctionnent correctement, mais dans Chrome, toutes les requêtes AJAX (qui sont nombreuses) sont envoyées en utilisant OPTIONS au lieu de la méthode définie que je lui donne. Techniquement, mes demandes sont "interdomaines". Le site est servi sur localhost: 6120 et le service auquel je fais des requêtes AJAX est sur 57124. Ce bogue jquery fermé définit le problème, mais pas un vrai correctif.

Que puis-je faire pour utiliser la méthode http appropriée dans les requêtes ajax?

Éditer:

C'est dans le chargement du document de chaque page:

jQuery.support.cors = true;

Et chaque AJAX est construit de la même manière:

var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
    url: url,
    dataType: "json",
    data: json,
    async: true,
    cache: false,
    timeout: 30000,
    headers: { "x-li-format": "json", "X-UserName": userName },
    success: function (data) {
        // my success stuff
    },
    error: function (request, status, error) {
        // my error stuff
    },
    type: "POST"
});
Corey Ogburn
la source
2
Le dernier commentaire de ce rapport de bogue l'explique assez bien ...
Kevin B
1
Cela m'a renversé parce que tout ce que je fais est tellement vanille (et mon code est similaire à celui du bogue jquery). Cela mis à part, ce n'est pas une excuse pour ne pas l'inclure. BRB, récupérant un exemple de code.
Corey Ogburn
3
Notez que IE ne prend pas en compte les numéros de port pour déterminer si une demande est d'origine croisée.
Ray Nicholus
@KevinB: Notre service REST tire parti de différentes demandes en faisant différentes choses basées sur la méthode http. Tout basculer vers GET n'est pas une solution valide. De plus, selon la réponse de Dark Falcon, cela n'aidera pas de toute façon car j'ai X-UserName et d'autres en-têtes personnalisés dans les demandes.
Corey Ogburn
cela ne change pas le fait que si vous voulez faire une demande cross-origin, vous devez suivre toutes les règles qui sont applicables aux demandes cross-origin pour que cela fonctionne correctement. les demandes cross-origin impliquent généralement une demande OPTIONS. Manipulez-le correctement et le problème disparaîtra. La seule autre façon de résoudre ce problème (sans changer l'API) est d'avoir un script sur le même serveur que la page principale qui interagit avec l'API en utilisant du code côté serveur.
Kevin B

Réponses:

136

Chrome effectue le contrôle en amont de la demande de recherche des en- têtes CORS . Si la demande est acceptable, elle enverra alors la vraie demande. Si vous faites ce cross-domain, vous devrez simplement vous en occuper ou bien trouver un moyen de rendre la requête non-cross-domain. C'est pourquoi le bogue jQuery a été fermé car non corrigé. C'est par conception.

Contrairement aux requêtes simples (discutées ci-dessus), les requêtes "pré-contrôlées" envoient d'abord une requête HTTP par la méthode OPTIONS à la ressource de l'autre domaine, afin de déterminer si la requête réelle peut être envoyée en toute sécurité. Les demandes intersites sont contrôlées en amont comme ceci, car elles peuvent avoir des implications sur les données des utilisateurs. En particulier, une demande est contrôlée en amont si:

  • Il utilise des méthodes autres que GET, HEAD ou POST. De plus, si POST est utilisé pour envoyer des données de requête avec un Content-Type autre que application / x-www-form-urlencoded, multipart / form-data ou text / plain, par exemple si la requête POST envoie une charge XML au serveur en utilisant application / xml ou text / xml, la demande est alors contrôlée en amont.
  • Il définit des en-têtes personnalisés dans la demande (par exemple, la demande utilise un en-tête tel que X-PINGOTHER)
Faucon noir
la source
20
En-têtes personnalisés. C'est probablement ce qui déclenche les appels OPTIONS en amont.
Corey Ogburn
18

Sur la base du fait que la requête n'est pas envoyée sur le port par défaut 80/443, cet appel Ajax est automatiquement considéré comme une requête de ressource cross-origin (CORS) , ce qui signifie que la requête émet automatiquement une requête OPTIONS qui vérifie En-têtes CORS du côté serveur / servlet.

Cela se produit même si vous définissez

crossOrigin: false;

ou même si vous l'omettez.

La raison est simplement que localhost != localhost:57124. Essayez de l'envoyer uniquement à localhostsans le port - cela échouera, car la cible demandée ne sera pas joignable, mais notez que si les noms de domaine sont égaux, la demande est envoyée sans la demande OPTIONS avant POST.

Abandonner
la source
3

Je suis d'accord avec Kevin B, le rapport de bogue dit tout. Il semble que vous essayez de faire des appels ajax interdomaines. Si vous n'êtes pas familier avec la même politique d'origine, vous pouvez commencer ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript .

Si cela n'est pas destiné à être un appel ajax inter-domaines, essayez de rendre votre URL cible relative et voyez si le problème disparaît. Si vous êtes vraiment désespéré, regardez le JSONP, mais méfiez-vous, le chaos se cache. Nous ne pouvons vraiment pas faire grand-chose de plus pour vous aider.

secousse
la source
1
La structure de notre système est quelque chose que je ne peux pas changer. L'utilisation d'un port différent est une exigence de notre architecture. J'obtiens la même politique d'origine mais je pense que le CORS que nous avons mis en œuvre était suffisant. Apparemment non.
Corey Ogburn
2
Si votre serveur renvoie des réponses JSON, vous pouvez consulter la méthode JSONP, utilisez-la simplement de manière responsable.
jgitter le
1
Je n'ai pas vraiment envie de discuter avec vous, mais JSONP utilise des balises de script pour extraire des données d'un autre domaine, puis envoie le résultat à une fonction de rappel. C'est beaucoup plus difficile si le résultat n'est pas json.
jgitter le
1
Non, ce n'est pas beaucoup plus difficile. En fait, la réponse ne doit en aucun cas être un JSON valide. Au lieu de cela, le serveur doit retourner quelque chose comme ceci: callbackfunc(somedata). Comme vous pouvez le voir, ce n'est pas du JSON valide. Et, somedatapeut être une chaîne, ou un nombre, ou tout ce que vous voulez.
Ray Nicholus
1
J'utilise Postman et là, les méthodes de requête sont envoyées correctement (par exemple «PUT», «DELETE», etc.). Mais quand j'essaie de le faire à partir de mon code, il les envoie toujours avec la méthode de demande OPTIONS. Je ne sais pas comment Postman est capable de le faire.
ErwinGO
1

Si cela est possible, passez les paramètres via GET / POST normal avec un nom différent et laissez le code côté serveur le gérer.

J'ai eu un problème similaire avec mon propre proxy pour contourner CORS et j'ai eu la même erreur de POST-> OPTION dans Chrome. C'était l'en- Authorizationtête dans mon cas ( "x-li-format"et "X-UserName"ici dans votre cas.) J'ai fini par le transmettre dans un format factice (par exemple AuthorizatinJackdans GET) et j'ai changé le code de mon proxy pour le transformer en en-tête lors de l'appel à la destination . Le voici en PHP:

if (isset($_GET['AuthorizationJack'])) {
    $request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}
Aide à
la source
1

Dans mon cas, j'appelle une API hébergée par AWS (API Gateway). L'erreur s'est produite lorsque j'ai essayé d'appeler l'API à partir d'un domaine autre que le propre domaine de l'API. Puisque je suis le propriétaire de l'API, j'ai activé CORS pour l'environnement de test, comme décrit dans la documentation Amazon .

En production, cette erreur ne se produira pas, car la demande et l'API seront dans le même domaine.

J'espère que ça aide!

gbonesso
la source
0

Comme l'a répondu @Dark Falcon, je l'ai simplement traité .

Dans mon cas, j'utilise le serveur node.js et je crée une session si elle n'existe pas. Étant donné que la méthode OPTIONS ne contient pas les détails de session, elle a fini par créer une nouvelle session pour chaque demande de méthode POST.

Donc, dans ma routine d'application pour create-session-if-not-exist, je viens d'ajouter une vérification pour voir si la méthode est OPTIONS, et si c'est le cas, ignorez simplement la session de création de la partie:

    app.use(function(req, res, next) {
        if (req.method !== "OPTIONS") {
            if (req.session && req.session.id) {
                 // Session exists
                 next();
            }else{
                 // Create session
                 next();
          }
        } else {
           // If request method is OPTIONS, just skip this part and move to the next method.
           next(); 
        }
    }
Mahesh
la source
0

Les requêtes "pré-contrôlées" envoient d'abord une requête HTTP par la méthode OPTIONS à la ressource sur l'autre domaine, afin de déterminer si la requête réelle peut être envoyée en toute sécurité. Demandes intersites

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Noorullah
la source
1
Pourriez-vous ajouter un peu plus d'informations? Votre réponse ressemble à un commentaire. :)
Badacadabra
0

Pensez à utiliser axios

axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {

  if(res.data.error) {

  } else { 
    doAnything( res.data )
  }

}).catch(function (error) {
   doAnythingError(error)
});

J'ai eu ce problème en utilisant fetch et axios fonctionnait parfaitement.

Evhz
la source
5
Axios utilise également les premières OPTIONS
Skylin R
0

J'ai rencontré un problème très similaire. J'ai passé près d'une demi-journée à comprendre pourquoi tout fonctionne correctement dans Firefox et échoue dans Chrome. Dans mon cas, c'était à cause de champs dupliqués (ou peut-être mal saisis) dans mon en-tête de demande.

Andrew Tatomyr
la source
0

Utilisez fetch au lieu de XHR, la requête ne sera pas pré-allumée même si elle a un domaine croisé.

Fei Sun
la source
-1
 $.ajax({
            url: '###',
            contentType: 'text/plain; charset=utf-8',
            async: false,
            xhrFields: {
                withCredentials: true,
                crossDomain: true,
                Authorization: "Bearer ...."
            },

            method: 'POST',

            data: JSON.stringify( request ),
            success: function (data) {
                console.log(data);
            }
        });

le contentType: 'text / plain; charset = utf-8 ', ou simplement contentType:' text / plain ', fonctionne pour moi! Cordialement!!

David Lopes
la source
Qu'est-ce que cela a à voir avec la question?
Corey Ogburn
Salut, je pense que cela résout le problème dans le titre, avec ce type de contenu, vous passez la méthode OPTIONS. Regards
David Lopes
ContentType n'a rien à voir avec la méthode.
Corey Ogburn
Je sais ce que vous dites, mais essayez-le. selon le navigateur, votre type de contenu peut influencer votre demande et changer votre méthode!
David Lopes