Wie überspringe ich die OPTIONS Preflight-Anfrage?

92

Ich hatte eine PhoneGap-App entwickelt, die jetzt in eine mobile Website umgewandelt wird. Bis auf einen kleinen Fehler funktioniert alles reibungslos. Ich verwende eine bestimmte Drittanbieter-API über eine POST-Anfrage, die in der App einwandfrei funktioniert, in der Version der mobilen Website jedoch fehlschlägt.

Bei näherer Betrachtung scheint AngularJS (ich denke, der Browser tatsächlich) zuerst eine OPTIONS-Anfrage zu senden. Ich habe heute viel über CORS gelernt, aber ich kann anscheinend nicht herausfinden, wie ich es insgesamt deaktivieren kann. Ich habe keinen Zugriff auf diese API (daher sind Änderungen auf dieser Seite nicht möglich), aber sie haben die Domäne, an der ich arbeite, zu ihrem Access-Control-Allow-Origin-Header hinzugefügt.

Dies ist der Code, über den ich spreche:

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

Wie kann ich verhindern, dass der Browser (oder AngularJS) diese OPTIONS-Anforderung sendet, und einfach zur eigentlichen POST-Anforderung springen? Ich benutze AngularJS 1.2.0.

Danke im Voraus.

Bram Vandewalle
quelle

Antworten:

100

Der Preflight wird von Ihrem Content-Type von ausgelöst application/json. Der einfachste Weg, dies zu verhindern, besteht darin, den Inhaltstyp text/plainauf Ihren Fall festzulegen. application/x-www-form-urlencoded& multipart/form-dataInhaltstypen sind ebenfalls akzeptabel, aber Sie müssen Ihre Anforderungsnutzdaten natürlich entsprechend formatieren.

Wenn nach dieser Änderung immer noch ein Preflight angezeigt wird, fügt Angular der Anforderung möglicherweise auch einen X-Header hinzu.

Oder Sie haben Header (Authorization, Cache-Control ...), die es auslösen, siehe:

Ray Nicholus
quelle
9
Dies ist die richtige Antwort - Ihre Header für Inhaltstyp und Cache-Steuerung lösen eine Preflight-Anforderung aus. Ein einfaches GET mit einem Inhaltstyp von Text / Plain und einigen anderen sind die einzigen Möglichkeiten, eine nicht vorab geflogene Anforderung auszulösen. Siehe: developer.mozilla.org/en-US/docs/HTTP/…
Jeff Hubbard
Ah ja, Cache-Control vergessen.
Ray Nicholus
15
Ein benutzerdefinierter Header löst auch den Preflight aus.
Sébastien Deprez
1
Das Ändern des Inhaltstyps, um den OPTIONs-Test zu verhindern, ist nicht die Antwort. Der Inhaltstyp sollte unabhängig davon mit dem Inhaltstyp
übereinstimmen
Wenn der Browser ausgelöst wird und im Backend die HTTP-Methode OPTIONS blockiert ist, hat dies Auswirkungen darauf, dass der Browser die entsprechende API für POST / PUT nicht aufruft, da OPTIONS fehlgeschlagen ist?
P Satish Patro
16

Wie Ray sagte, können Sie es stoppen, indem Sie den Content-Header wie folgt ändern:

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

Zum Beispiel -

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'}
        });
    }]);

Oder direkt zu einem Anruf -

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

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

Dies sendet keine Anfrage für eine Option vor dem Flug.

HINWEIS: Die Anforderung sollte keinen benutzerdefinierten Header-Parameter haben. Wenn der Anforderungsheader einen benutzerdefinierten Header enthält, stellt der Browser eine Anforderung vor dem Flug. Sie können dies nicht vermeiden.

Vivex
quelle
1
Wie soll ich es nur mit machen $http?
Lernende
Danke, das ist ähnlich wie bei mir. Die einzigen Änderungen sind die Methode GETund ein zusätzlicher Header Authorization. Aber immer noch den Preflight senden.
Lernende
1
Können Sie Ihre Anfrage hier einfügen? als Locke oder so? Möglicherweise liegt es am Authorization-Header. Versuchen Sie, ihn zu entfernen, und versuchen Sie es dann. Wenn Sie benutzerdefinierte Header senden, sendet Angular eine Anfrage vor dem Flug.
Vivex
pastebin.com/vRDeFiH2 Die erste ist die Anfrage an $ http und die zweite ist eine gültige Anfrage, die vom Postboten erstellt wurde :)
learnercys
2

Ich denke, der beste Weg ist zu überprüfen, ob die Anfrage vom Typ "OPTIONEN" ist und 200 von der mittleren Ware zurückgibt. Es hat bei mir funktioniert.

express.use('*',(req,res,next) =>{
      if (req.method == "OPTIONS") {
        res.status(200);
        res.send();
      }else{
        next();
      }
    });
Devbrat Raghuvanshi
quelle
Es funktioniert, aber in OWASP wird empfohlen, OPTIONEN nicht verfügbar zu machen. Sie blockieren es im Backend / Hosted Service (Nginx, Apache) usw.
P Satish Patro
1

Bei der Ausführung bestimmter Arten von domänenübergreifenden AJAX-Anforderungen fügen moderne Browser, die CORS unterstützen, eine zusätzliche "Preflight" -Anforderung ein, um festzustellen, ob sie über die Berechtigung zum Ausführen der Aktion verfügen. Aus Beispielabfrage:

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

Als Ergebnis dieses Fragments können wir sehen, dass der Adresse zwei Anforderungen gesendet wurden (OPTIONS und GET). Die Antwort vom Server enthält Header, die die Zulässigkeit der Abfrage GET bestätigen. Wenn Ihr Server nicht für die ordnungsgemäße Verarbeitung einer OPTIONS-Anforderung konfiguriert ist, schlagen Clientanforderungen fehl. Beispielsweise:

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
quelle
0

Preflight ist eine vom Browser implementierte Web-Sicherheitsfunktion. Für Chrome können Sie die gesamte Websicherheit deaktivieren, indem Sie das Flag --disable-web-security hinzufügen.

Beispiel: "C: \ Programme \ Google \ Chrome \ Application \ chrome.exe" --disable-web-security --user-data-dir = "C: \ newChromeSettingsWithoutSecurity". Sie können zuerst eine neue Verknüpfung für Chrom erstellen, zu den Eigenschaften wechseln und das Ziel wie oben ändern. Das sollte helfen!

Arijit Patra
quelle
Sie können nicht wirklich erwarten, dass OP seinen Kunden sagt, dass sie die Browsersicherheit deaktivieren sollen, nur um eine Funktion zu aktivieren, oder?!
Svarog
@svarog Dies ist hauptsächlich für Entwicklungszwecke gedacht. Meistens treten Sie auf Produktionsservern nicht mit diesem Problem auf.
Arijit Patra
Wenn der Browser ausgelöst wird und im Backend die HTTP-Methode OPTIONS blockiert ist, hat dies Auswirkungen darauf, dass der Browser die entsprechende API für POST / PUT nicht aufruft, da OPTIONS fehlgeschlagen ist?
P Satish Patro
-2

Wenn Sie den Inhaltstyp auf undefiniert setzen, werden die Header-Daten so wie sie sind von Javascript übergeben und die standardmäßigen eckigen $ httpProvider-Header-Konfigurationen überschrieben. Angular $ http-Dokumentation

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