Mencoba menggunakan mode ambil dan lewati: no-cors


167

Saya dapat mencapai titik akhir ini, http://catfacts-api.appspot.com/api/facts?number=99melalui Postman dan kembaliJSON

Selain itu saya menggunakan create-react-app dan ingin menghindari pengaturan konfigurasi server.

Dalam kode klien saya, saya mencoba menggunakan fetchuntuk melakukan hal yang sama, tetapi saya mendapatkan kesalahan:

Header 'Access-Control-Allow-Origin' tidak ada pada sumber daya yang diminta. Karena itu, ' http: // localhost: 3000 ' tidak diizinkan mengakses. Jika respons buram memenuhi kebutuhan Anda, atur mode permintaan ke 'no-cors' untuk mengambil sumber daya dengan CORS dinonaktifkan.

Jadi saya mencoba mengirimkan objek, ke Ambil saya yang akan menonaktifkan CORS, seperti:

fetch('http://catfacts-api.appspot.com/api/facts?number=99', { mode: 'no-cors'})
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });

Cukup menarik kesalahan yang saya dapatkan sebenarnya adalah kesalahan sintaks dengan fungsi ini. Saya tidak yakin yang sebenarnyafetch rusak, karena ketika saya menghapus objek {mode: 'no-cors'}, dan menyediakannya dengan URL yang berbeda, ia berfungsi dengan baik.

Saya juga mencoba untuk meneruskan objek { mode: 'opaque'}, tetapi ini mengembalikan kesalahan asli dari atas.

Saya percaya semua yang harus saya lakukan adalah menonaktifkan CORS .. Apa yang saya lewatkan?

Jawaban:


292

mode: 'no-cors'tidak akan secara ajaib membuat sesuatu bekerja. Bahkan itu membuat segalanya lebih buruk, karena satu efek yang dimilikinya adalah memberi tahu browser, "Blokir kode JavaScript frontend saya agar tidak melihat isi badan respons dan tajuk dalam semua keadaan." Tentu saja Anda hampir tidak pernah menginginkannya.

Apa yang terjadi dengan permintaan lintas-asal dari JavaScript frontend adalah bahwa browser secara default memblokir kode frontend dari mengakses sumber-sumber silang. JikaAccess-Control-Allow-Origin ada dalam respons, maka browser akan mengendurkan pemblokiran itu dan memungkinkan kode Anda untuk mengakses respons.

Tetapi jika sebuah situs tidak mengirim Access-Control-Allow-Origintanggapan, kode frontend Anda tidak dapat langsung mengakses tanggapan dari situs itu. Secara khusus, Anda tidak dapat memperbaikinya dengan menentukan mode: 'no-cors'(pada kenyataannya itu akan memastikan kode frontend Anda tidak dapat mengakses konten respons).

Namun, satu hal yang akan berhasil: jika Anda mengirim permintaan Anda melalui proxy CORS , seperti ini:

var proxyUrl = 'https://cors-anywhere.herokuapp.com/',
    targetUrl = 'http://catfacts-api.appspot.com/api/facts?number=99'
fetch(proxyUrl + targetUrl)
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    document.querySelector("pre").innerHTML = JSON.stringify(data, null, 2);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });
<pre></pre>

Catatan: jika ketika Anda mencoba menggunakan https://cors-anywhere.herokuapp.com, Anda merasa sedang down , Anda juga dapat dengan mudah menggunakan proxy Anda sendiri untuk Heroku secara harfiah hanya 2-3 menit, dengan 5 perintah:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

Setelah menjalankan perintah itu, Anda akan berakhir dengan server CORS Anywhere Anda berjalan di, mis., Https://cryptic-headland-94862.herokuapp.com/ . Jadi, alih-alih awalan dengan URL permintaan Anda https://cors-anywhere.herokuapp.com, awali dengan URL untuk instance Anda sendiri; misalnya, https://cryptic-headland-94862.herokuapp.com/https://example.com .


Saya dapat mencapai titik akhir ini, http://catfacts-api.appspot.com/api/facts?number=99melalui Postman

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS menjelaskan mengapa meskipun Anda dapat mengakses respons dengan Postman, browser tidak akan membiarkan Anda mengakses lintas sumber tanggapan dari frontend Kode JavaScript berjalan di aplikasi web kecuali jika respons menyertakan Access-Control-Allow-Originheader respons.

http://catfacts-api.appspot.com/api/facts?number=99 tidak memiliki Access-Control-Allow-Origintajuk respons, jadi tidak mungkin kode frontend Anda dapat mengakses asal lintas respons.

Browser Anda dapat memperoleh respons yang baik dan Anda dapat melihatnya di Postman dan bahkan di devtools browser — tetapi itu tidak berarti browser akan memaparkannya ke kode Anda. Mereka tidak akan melakukannya, karena tidak memiliki Access-Control-Allow-Origintajuk respons. Jadi, Anda harus menggunakan proxy untuk mendapatkannya.

Proxy membuat permintaan ke situs itu, mendapatkan respons, menambahkan Access-Control-Allow-Originheader respons dan header CORS lainnya yang diperlukan, lalu meneruskannya kembali ke kode permintaan Anda. Dan respons dengan Access-Control-Allow-Origintajuk yang ditambahkan adalah yang dilihat browser, sehingga browser memungkinkan kode frontend Anda mengakses respons.


Jadi saya mencoba mengirimkan objek, ke Ambil saya yang akan menonaktifkan CORS

Anda tidak ingin melakukan itu. Untuk menjadi jelas, ketika Anda mengatakan Anda ingin "menonaktifkan CORS" sepertinya Anda benar-benar ingin menonaktifkan kebijakan yang sama-asal . CORS sendiri sebenarnya adalah cara untuk melakukan itu - CORS adalah cara untuk melonggarkan kebijakan yang sama-asal, bukan cara untuk membatasi itu.

Tapi bagaimanapun, memang benar Anda dapat - hanya di lingkungan lokal Anda - melakukan hal-hal seperti memberikan bendera runtime browser Anda untuk menonaktifkan keamanan dan menjalankan dengan tidak aman, atau Anda dapat menginstal ekstensi browser secara lokal untuk menyiasati kebijakan yang sama-asal-usulnya, tetapi semua itu tidak adalah mengubah situasi hanya untuk Anda secara lokal.

Tidak peduli apa yang Anda ubah secara lokal, siapa pun yang mencoba menggunakan aplikasi Anda masih akan menjalankan kebijakan asal yang sama, dan tidak ada cara Anda dapat menonaktifkannya untuk pengguna lain dari aplikasi Anda.

Anda kemungkinan besar tidak ingin menggunakannya mode: 'no-cors'dalam praktik kecuali dalam beberapa kasus terbatas , dan bahkan hanya jika Anda tahu persis apa yang Anda lakukan dan apa efeknya. Itu karena apa yang mode: 'no-cors'sebenarnya dikatakan oleh pengaturan pada browser adalah, "Blokir kode JavaScript frontend saya untuk melihat isi dari badan respon dan header dalam segala keadaan." Dalam kebanyakan kasus, hal itu jelas bukan yang Anda inginkan.


Sejauh kasus ketika Anda akan ingin mempertimbangkan untuk menggunakan mode: 'no-cors', melihat jawaban di Apa keterbatasan berlaku untuk tanggapan buram? untuk detailnya. Intinya adalah bahwa kasusnya adalah:

  • Dalam kasus terbatas ketika Anda menggunakan JavaScript untuk menempatkan konten dari asal yang lain menjadi <script>, <link rel=stylesheet>, <img>, <video>, <audio>, <object>, <embed>, atau <iframe>elemen (yang bekerja karena embedding sumber daya lintas-asal diperbolehkan bagi mereka) - tapi untuk beberapa alasan Anda don' t ingin atau tidak bisa melakukan itu hanya dengan memiliki markup dokumen menggunakan URL sumber daya sebagai hrefatau srcatribut untuk elemen.

  • Ketika satu-satunya hal yang ingin Anda lakukan dengan sumber daya adalah untuk menyimpannya. Seperti disinggung dalam jawaban. Batasan apa yang berlaku untuk tanggapan yang tidak jelas? , dalam praktiknya skenario yang berlaku adalah ketika Anda menggunakan Pekerja Layanan, dalam hal ini API yang relevan adalah API Penyimpanan Cache .

Tetapi bahkan dalam kasus-kasus terbatas itu, ada beberapa hal penting yang harus diperhatikan; lihat jawabannya di Batasan apa yang berlaku untuk respons buram? untuk detailnya.


Saya juga telah mencoba untuk melewati objek { mode: 'opaque'}

Tidak ada mode: 'opaque'mode permintaan - opaquealih-alih hanya properti respons , dan browser menetapkan properti buram pada respons dari permintaan yang dikirim denganno-cors mode.

Tetapi secara kebetulan kata opak adalah sinyal yang cukup eksplisit tentang sifat respons yang Anda dapatkan: “buram” berarti Anda tidak dapat melihatnya.


4
Sukai cors-anywheresolusinya untuk kasus-kasus penggunaan non-prod sederhana (yaitu mengambil beberapa data yang tersedia untuk umum). Jawaban ini mengonfirmasi kecurigaan saya bahwa no-corsini tidak umum karena OpaqueResponse-nya tidak terlalu berguna; yaitu "kasus yang sangat terbatas"; adakah yang bisa menjelaskan kepada saya contoh mana no-corsyang berguna?
Kacang Merah

1
@TheRedPea Lihat pembaruan yang saya buat untuk jawaban di sini (dan saya juga menambahkan sebagai komentar untuk pertanyaan Anda di stackoverflow.com/questions/52569895/… )
sontonbarker

Saya perlu mengonfigurasi server Express saya dengan paket CORS.js - github.com/expressjs/cors - dan kemudian saya harus menghapus mode: 'no-cors'dari permintaan pengambilan (jika tidak, responsnya akan kosong)
James L.

proxy CORS hebat. Saya kagum bahwa itu dianggap sebagai langkah "keamanan" untuk memblokir akses ke file publik sama sekali. Ini sangat, sangat mudah untuk berkeliling dari sudut pandang hacker, dan memang itulah yang dilakukannya. Ini benar-benar hanya merepotkan bagi aktor yang baik.
Seph Reed

Saya menghasilkan kode klien berbeda yang menggunakan API yang sama. Saya menggunakannya untuk pelatihan. Saya ingin menjaga kode tetap sederhana dan membiarkan pengguna memainkannya. Saya perlu menggunakan no-cros.
profimedica

6

Jadi jika Anda seperti saya dan mengembangkan situs web di localhost tempat Anda mencoba mengambil data dari Laravel API dan menggunakannya di front-end Vue Anda, dan Anda melihat masalah ini, berikut adalah cara saya menyelesaikannya:

  1. Dalam proyek Laravel Anda, jalankan perintah php artisan make:middleware Cors. Ini akan membuat app/Http/Middleware/Cors.phpuntuk Anda.
  2. Tambahkan kode berikut di dalam handlesfungsi di Cors.php:

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  3. Di app/Http/kernel.php, tambahkan entri berikut dalam $routeMiddlewarearray:

    cors => \App\Http\Middleware\Cors::class

    (Akan ada entri lain dalam array seperti auth, guestdll. Juga pastikan Anda melakukan ini app/Http/kernel.phpkarena ada yang lain kernel.phpjuga di Laravel)

  4. Tambahkan middleware ini pada pendaftaran rute untuk semua rute di mana Anda ingin mengizinkan akses, seperti ini:

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
  5. Di front-end Vue, pastikan Anda memanggil API ini dalam mounted()fungsi dan bukan di data(). Pastikan juga Anda menggunakan http://atau https://dengan URL dalam fetch()panggilan Anda .

Penghargaan penuh untuk artikel blog Pete Houston .


2

Solusi sederhana: Tambahkan berikut ini ke bagian paling atas dari file php tempat Anda meminta data.

header("Access-Control-Allow-Origin: *");


8
Ini adalah ide yang sangat bagus jika Anda menginginkan jumlah keamanan sekecil mungkin :).
Bidah Monyet

bagaimana dengan hotlink gambar
user889030

1

Solusi bagi saya adalah hanya melakukannya di sisi server

Saya menggunakan WebClientperpustakaan C # untuk mendapatkan data (dalam kasus saya itu adalah data gambar) dan mengirimkannya kembali ke klien. Mungkin ada sesuatu yang sangat mirip dalam bahasa sisi server pilihan Anda.

//Server side, api controller

[Route("api/ItemImage/GetItemImageFromURL")]
public IActionResult GetItemImageFromURL([FromQuery] string url)
{
    ItemImage image = new ItemImage();

    using(WebClient client = new WebClient()){

        image.Bytes = client.DownloadData(url);

        return Ok(image);
    }
}

Anda dapat mengubahnya untuk apa pun use case Anda sendiri. Poin utama client.DownloadData()berfungsi tanpa kesalahan CORS. Biasanya masalah CORS hanya antar situs web, karenanya boleh saja membuat permintaan 'lintas situs' dari server Anda.

Kemudian React fetch call sesederhana:

//React component

fetch(`api/ItemImage/GetItemImageFromURL?url=${imageURL}`, {            
        method: 'GET',
    })
    .then(resp => resp.json() as Promise<ItemImage>)
    .then(imgResponse => {

       // Do more stuff....
    )}

1

Solusi yang sangat mudah (2 menit untuk mengkonfigurasi) adalah dengan menggunakan paket local-ssl-proxy darinpm

Penggunaannya lurus ke depan:
1. Instal paket: npm install -g local-ssl-proxy
2. Sambil menjalankan local-servertopeng Anda denganlocal-ssl-proxy --source 9001 --target 9000

PS: Ganti --target 9000dengan -- "number of your port"dan --source 9001dengan--source "number of your port +1"


1
Saya punya masalah menggunakan aplikasi saya di perangkat ponsel nyata. Apakah solusi Anda bermanfaat untuk kasus ini?
Hamid Araghi

@ HamidAraghi Saya akan mengira bahwa sekarang, karena untuk tujuan pengembangan server proxy harus berjalan pada perangkat yang sebenarnya dipengaruhi oleh kesalahan
volna
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.