Bagaimana cara melewati permintaan OPTIONS preflight?

93

Saya telah mengembangkan aplikasi PhoneGap yang sekarang sedang diubah menjadi situs web seluler. Semuanya bekerja dengan lancar selain satu kesalahan kecil. Saya menggunakan API pihak ketiga tertentu melalui permintaan POST, yang berfungsi dengan baik di aplikasi, tetapi gagal dalam versi situs web seluler.

Setelah melihat lebih dekat, sepertinya AngularJS (saya kira browser sebenarnya) pertama kali mengirimkan permintaan OPTIONS. Saya belajar banyak hari ini tentang CORS, tetapi sepertinya saya tidak tahu cara menonaktifkannya sama sekali. Saya tidak memiliki akses ke API itu (jadi perubahan di sisi itu tidak mungkin), tetapi mereka telah menambahkan domain yang saya kerjakan ke header Access-Control-Allow-Origin mereka.

Ini adalah kode yang saya bicarakan:

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

Bagaimana cara mencegah browser (atau AngularJS) mengirim permintaan OPTIONS itu dan langsung saja ke permintaan POST yang sebenarnya? Saya menggunakan AngularJS 1.2.0.

Terima kasih sebelumnya.

Bram Vandewalle
sumber

Jawaban:

100

Preflight dipicu oleh Jenis Konten Anda application/json. Cara termudah untuk mencegah hal ini adalah dengan menyetel Jenis Konten sesuai text/plainkeinginan Anda. application/x-www-form-urlencoded& multipart/form-dataJenis Konten juga dapat diterima, tetapi Anda tentu saja harus memformat muatan permintaan Anda dengan tepat.

Jika Anda masih melihat preflight setelah melakukan perubahan ini, Angular mungkin juga menambahkan X-header ke permintaan tersebut.

Atau Anda mungkin memiliki header (Otorisasi, Cache-Control ...) yang akan memicunya, lihat:

Ray Nicholus
sumber
9
Ini adalah jawaban yang benar - header Content-Type dan Cache-Control Anda memicu permintaan preflight. GET biasa dengan Jenis Konten teks / biasa dan beberapa lainnya adalah satu-satunya cara untuk memicu permintaan non-preflighted. Lihat: developer.mozilla.org/en-US/docs/HTTP/…
Jeff Hubbard
Ah ya, lupakan tentang Kontrol-Cache.
Ray Nicholus
17
Header kustom juga akan memicu preflight.
Sébastien Deprez
2
Mengubah tipe konten untuk mencegah tes OPTION bukanlah jawabannya. Jenis konten harus cocok dengan jenis konten apapun
ekerner
jika browser melempar, & di backend, metode Http OPSI diblokir, apakah akan berpengaruh seperti browser tidak akan memanggil API yang sesuai untuk POST / PUT karena OPSI gagal?
P Satish Patro
16

Seperti yang dikatakan Ray, Anda dapat menghentikannya dengan memodifikasi header konten seperti -

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

Sebagai contoh -

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

Atau langsung ke panggilan -

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

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

Ini tidak akan mengirimkan permintaan opsi pra-penerbangan.

CATATAN: Permintaan tidak boleh memiliki parameter tajuk khusus, Jika tajuk permintaan berisi tajuk khusus apa pun maka browser akan membuat permintaan pra-penerbangan, Anda tidak dapat menghindarinya.

vivex
sumber
1
Bagaimana saya harus melakukannya hanya dengan $http?
learnercys
Terima kasih, itu mirip dengan yang saya lakukan. Satu-satunya perubahan adalah metode GETdan header tambahan Authorization. Tapi tetap mengirim preflight.
learnercys
1
Bisakah Anda menempelkan permintaan Anda di sini? sebagai ikal atau sesuatu? Mungkin karena header Otorisasi, coba hapus lalu coba. Jika Anda mengirim tajuk khusus, angular akan mengirim permintaan pra-penerbangan.
vivex
pastebin.com/vRDeFiH2 Yang pertama adalah permintaan ke $ http dan yang kedua adalah permintaan valid yang dibuat oleh tukang pos :)
learnercys
2

Saat melakukan jenis permintaan AJAX lintas domain tertentu, browser modern yang mendukung CORS akan memasukkan permintaan "preflight" tambahan untuk menentukan apakah mereka memiliki izin untuk melakukan tindakan tersebut. Dari contoh kueri:

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

Sebagai hasil dari fragmen ini kita dapat melihat bahwa alamat tersebut dikirim dua permintaan (OPTIONS dan GET). Tanggapan dari server termasuk tajuk yang mengkonfirmasikan izin permintaan GET. Jika server Anda tidak dikonfigurasi untuk memproses permintaan OPTIONS dengan benar, permintaan klien akan gagal. Sebagai contoh:

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
sumber
2

Saya pikir cara terbaik adalah memeriksa apakah permintaan jenis "OPTIONS" kembali 200 dari perangkat menengah. Itu berhasil untuk saya.

express.use('*',(req,res,next) =>{
      if (req.method == "OPTIONS") {
        res.status(200);
        res.send();
      }else{
        next();
      }
    });
Devbrat Raghuvanshi
sumber
Ini berfungsi tetapi di OWASP disarankan untuk tidak mengekspos OPTIONS. Anda memblokir cuaca di layanan backend / host (Nginx, Apache) dll.
P Satish Patro
0

Preflight adalah fitur keamanan web yang diimplementasikan oleh browser. Untuk Chrome, Anda dapat menonaktifkan semua keamanan web dengan menambahkan tanda --disable-web-security.

Misalnya: "C: \ Program Files \ Google \ Chrome \ Application \ chrome.exe" --disable-web-security --user-data-dir = "C: \ newChromeSettingsWithoutSecurity". Pertama Anda dapat membuat pintasan baru dari chrome, buka propertinya dan ubah target seperti di atas. Ini akan membantu!

Arijit Patra
sumber
Anda tidak dapat mengharapkan OP memberi tahu kliennya untuk mematikan keamanan browser hanya untuk mengaktifkan fitur, bukan ?!
svarog
@svarog ini sebagian besar untuk tujuan dev, sebagian besar di server produksi Anda tidak akan menghadapi masalah ini.
Arijit Patra
jika browser melempar, & di backend, metode Http OPSI diblokir, apakah akan berpengaruh seperti browser tidak akan memanggil API yang sesuai untuk POST / PUT karena OPSI gagal?
P Satish Patro
-2

mengatur tipe konten ke tidak terdefinisi akan membuat javascript melewatkan data header sebagaimana adanya, dan menulis konfigurasi header default $ httpProvider. Dokumentasi $ http sudut

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