Comment ignorer la demande de contrôle en amont OPTIONS?

93

J'avais développé une application PhoneGap qui est en train d'être transformée en site Web mobile. Tout fonctionne bien à part un petit problème. J'utilise une certaine API tierce via une requête POST, qui fonctionne bien dans l'application, mais échoue dans la version du site Web mobile.

Après un examen plus approfondi, il semble qu'AngularJS (je suppose que le navigateur en fait) envoie d'abord une requête OPTIONS. J'ai beaucoup appris aujourd'hui sur CORS, mais je n'arrive pas à comprendre comment le désactiver complètement. Je n'ai pas accès à cette API (donc les changements de ce côté sont impossibles), mais ils ont ajouté le domaine sur lequel je travaille à leur en-tête Access-Control-Allow-Origin.

C'est le code dont je parle:

        var request = {
                language: 'fr',
                barcodes: [
                    {
                        barcode: 'somebarcode',
                        description: 'Description goes here'
                    }
                ]
            };
        }
        var config = {
            headers: { 
                'Cache-Control': 'no-cache',
                'Content-Type': 'application/json'
            }
        };
        $http.post('http://somedomain.be/trackinginfo', request, config).success(function(data, status) {
            callback(undefined, data);
        }).error(function(data, status) {
            var err = new Error('Error message');
            err.status = status;
            callback(err);
        });

Comment puis-je empêcher le navigateur (ou AngularJS) d'envoyer cette requête OPTIONS et passer simplement à la requête POST réelle? J'utilise AngularJS 1.2.0.

Merci d'avance.

Bram Vandewalle
la source

Réponses:

100

Le contrôle en amont est déclenché par votre type de contenu de application/json. Le moyen le plus simple d'éviter cela est de définir le type de contenu text/plaindans votre cas. application/x-www-form-urlencoded& multipart/form-dataLes types de contenu sont également acceptables, mais vous devrez bien sûr formater la charge utile de votre requête de manière appropriée.

Si vous voyez toujours un contrôle en amont après avoir apporté cette modification, Angular peut également ajouter un en-tête X à la demande.

Ou vous pourriez avoir des en-têtes (Authorization, Cache-Control ...) qui le déclencheront, voir:

Ray Nicholus
la source
9
C'est la bonne réponse - vos en-têtes Content-Type et Cache-Control déclenchent une demande de contrôle en amont. Un GET simple avec un Content-Type text / plain et quelques autres sont les seuls moyens de déclencher une requête non contrôlée en amont. Voir: developer.mozilla.org/en-US/docs/HTTP/…
Jeff Hubbard
Ah oui, j'ai oublié Cache-Control.
Ray Nicholus
15
Un en-tête personnalisé déclenchera également le contrôle en amont.
Sébastien Deprez
1
Changer le type de contenu pour empêcher le test OPTIONs n'est pas la solution. Le type de contenu doit correspondre au type de contenu quel que soit
ekerner
si c'est le navigateur qui lance, et dans le backend, la méthode Http OPTIONS est bloquée, cela aura-t-il un effet comme le navigateur n'appellera pas l'API correspondante pour POST / PUT car OPTIONS a échoué?
P Satish Patro le
16

Comme l'a dit Ray, vous pouvez l'arrêter en modifiant l'en-tête de contenu comme -

 $http.defaults.headers.post["Content-Type"] = "text/plain";

Par exemple -

angular.module('myApp').factory('User', ['$resource','$http',
    function($resource,$http){
        $http.defaults.headers.post["Content-Type"] = "text/plain";
        return $resource(API_ENGINE_URL+'user/:userId', {}, {
            query: {method:'GET', params:{userId:'users'}, isArray:true},
            getLoggedIn:{method:'GET'}
        });
    }]);

Ou directement à un appel -

var req = {
 method: 'POST',
 url: 'http://example.com',
 headers: {
   'Content-Type': 'text/plain'
 },
 data: { test: 'test' }
}

$http(req).then(function(){...}, function(){...});

Cela n'enverra aucune demande d'option avant le vol.

REMARQUE: la demande ne doit pas avoir de paramètre d'en-tête personnalisé.Si l'en-tête de la demande contient un en-tête personnalisé, le navigateur effectuera une demande avant le vol, vous ne pouvez pas l'éviter.

vivex
la source
1
Comment dois-je le faire avec $http?
learnercys
Merci, c'est similaire à ce que je faisais. Les seuls changements sont la méthode GETet un en-tête supplémentaire Authorization. Mais toujours l'envoi du contrôle en amont.
learnercys
1
Pouvez-vous coller votre demande ici? comme curl ou quelque chose? Peut-être que c'est à cause de l'en-tête d'autorisation, essayez de le supprimer, puis essayez. Si vous envoyez des en-têtes personnalisés, angular enverra une demande de pré-vol.
vivex
pastebin.com/vRDeFiH2 Le premier est la requête à $ http et le second est une requête valide construit par le facteur :)
learnercys
2

Lors de l'exécution de certains types de requêtes AJAX inter-domaines, les navigateurs modernes qui prennent en charge CORS insèrent une requête supplémentaire de "contrôle en amont" pour déterminer s'ils sont autorisés à effectuer l'action. À partir d'un exemple de requête:

$http.get( https://example.com/api/v1/users/’ +userId,
  {params:{
           apiKey:’34d1e55e4b02e56a67b0b66
          }
  } 
);

À la suite de ce fragment, nous pouvons voir que l'adresse a été envoyée deux requêtes (OPTIONS et GET). La réponse du serveur comprend des en-têtes confirmant la validité de la requête GET. Si votre serveur n'est pas configuré pour traiter correctement une requête OPTIONS, les requêtes client échoueront. Par exemple:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: accept, origin, x-requested-with, content-type
Access-Control-Allow-Methods: DELETE
Access-Control-Allow-Methods: OPTIONS
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Methods: GET
Access-Control-Allow-Methods: POST
Access-Control-Allow-Orgin: *
Access-Control-Max-Age: 172800
Allow: PUT
Allow: OPTIONS
Allow: POST
Allow: DELETE
Allow: GET
M.Void
la source
2

Je pense que le meilleur moyen est de vérifier si la demande est de type "OPTIONS" return 200 du middleware. Cela a fonctionné pour moi.

express.use('*',(req,res,next) =>{
      if (req.method == "OPTIONS") {
        res.status(200);
        res.send();
      }else{
        next();
      }
    });
Devbrat Raghuvanshi
la source
Cela fonctionne mais dans OWASP, il est recommandé de ne pas exposer OPTIONS. Vous météo le bloquer dans le backend / service hébergé (Nginx, Apache) etc.
P Satish Patro
0

Le contrôle en amont est une fonctionnalité de sécurité Web implémentée par le navigateur. Pour Chrome, vous pouvez désactiver toute la sécurité Web en ajoutant l'indicateur --disable-web-security.

Par exemple: "C: \ Program Files \ Google \ Chrome \ Application \ chrome.exe" --disable-web-security --user-data-dir = "C: \ newChromeSettingsWithoutSecurity". Vous pouvez d'abord créer un nouveau raccourci de chrome, accéder à ses propriétés et modifier la cible comme ci-dessus. Cela devrait aider!

Arijit Patra
la source
Vous ne pouvez pas vraiment vous attendre à ce qu'OP dise à ses clients de désactiver la sécurité du navigateur juste pour activer une fonctionnalité, non ?!
svarog le
@svarog c'est principalement à des fins de développement, principalement sur le serveur de production, vous ne serez pas confronté à ce problème.
Arijit Patra le
si c'est le navigateur qui lance, et dans le backend, la méthode Http OPTIONS est bloquée, cela aura-t-il un effet comme le navigateur n'appellera pas l'API correspondante pour POST / PUT car OPTIONS a échoué?
P Satish Patro le
-2

définir le type de contenu sur undefined obligerait javascript à transmettre les données d'en-tête telles quelles, et à écraser les configurations d'en-tête angulaires par défaut $ httpProvider. Documentation Angular $ http

$http({url:url,method:"POST", headers:{'Content-Type':undefined}).then(success,failure);
Theophilus Omoregbee
la source