Header 'Access-Control-Allow-Origin' tidak ada pada sumber daya yang diminta — ketika mencoba untuk mendapatkan data dari REST API

469

Saya mencoba mengambil beberapa data dari REST API HP Alm. Ini bekerja cukup baik dengan skrip ikal kecil - saya mendapatkan data saya.

Sekarang melakukan itu dengan JavaScript, ambil dan ES6 (kurang lebih) tampaknya menjadi masalah yang lebih besar. Saya terus menerima pesan kesalahan ini:

Ambil API tidak dapat memuat. Respons terhadap permintaan preflight tidak lulus pemeriksaan kontrol akses: Tidak ada tajuk 'Akses-Kontrol-Bolehkan-Asal' hadir pada sumber daya yang diminta. Origin ' http://127.0.0.1:3000 ' karenanya tidak diperbolehkan mengakses. Responsnya memiliki kode status HTTP 501. Jika respons buram memenuhi kebutuhan Anda, atur mode permintaan ke 'no-cors' untuk mengambil sumber daya dengan CORS dinonaktifkan.

Saya mengerti bahwa ini karena saya mencoba mengambil data dari dalam localhost saya dan solusinya harus menggunakan CORS. Sekarang saya pikir saya benar-benar melakukan itu, tetapi entah bagaimana mengabaikan apa yang saya tulis di header atau masalahnya adalah hal lain?

Jadi, apakah ada masalah implementasi? Apakah saya salah? Sayangnya saya tidak dapat memeriksa log server. Saya agak macet di sini.

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

Saya menggunakan Chrome. Saya juga mencoba menggunakan Plugin Chrome CORS itu, tetapi kemudian saya mendapatkan pesan kesalahan lain:

Nilai tajuk 'Akses-Kontrol-Bolehkan-Asal' dalam respons tidak boleh berupa wildcard '*' ketika mode kredensial permintaan adalah 'sertakan'. Origin ' http://127.0.0.1:3000 ' karenanya tidak diperbolehkan mengakses. Mode kredensial permintaan yang dimulai oleh XMLHttpRequest dikendalikan oleh atribut withCredentials.

daniel.lozynski
sumber

Jawaban:

825

Jawaban ini mencakup banyak landasan, sehingga terbagi menjadi tiga bagian:

  • Cara menggunakan proxy CORS untuk berkeliling “Tidak Access-Control-Allow-Origin header” masalah
  • Cara menghindari preflight CORS
  • Cara untuk memperbaiki “Access-Control-Allow-Origin sundulan tidak harus menjadi wildcard” masalah

Cara menggunakan proxy CORS untuk berkeliling “Tidak Access-Control-Allow-Origin header” masalah

Jika Anda tidak mengontrol server, kode JavaScript frontend Anda mengirim permintaan ke, dan masalah dengan respons dari server itu hanya kurangnya Access-Control-Allow-Originheader yang diperlukan , Anda masih bisa menyelesaikan pekerjaan — dengan membuat permintaan melalui Proxy CORS. Untuk menunjukkan cara kerjanya, pertama-tama inilah beberapa kode yang tidak menggunakan proxy CORS:

const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

Alasan catchpemblokiran ada di sana adalah, browser mencegah kode itu dari mengakses tanggapan yang datang kembali https://example.com. Dan alasan browser melakukan itu adalah, responsnya kurang memiliki Access-Control-Allow-Originheader respons.

Sekarang, inilah contoh yang persis sama tetapi hanya dengan proxy CORS ditambahkan:

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

Catatan: Jika https://cors-anywhere.herokuapp.com sedang down atau tidak tersedia saat Anda mencobanya, maka lihat di bawah ini bagaimana cara menyebarkan server CORS Anywhere Anda sendiri di Heroku hanya dalam 2-3 menit.

Cuplikan kode kedua di atas dapat mengakses respons dengan sukses karena mengambil URL permintaan dan mengubahnya ke https://cors-anywhere.herokuapp.com/https://example.com —dengan hanya mengawalinya dengan URL proxy — menyebabkan meminta dilakukan melalui proxy itu, yang kemudian:

  1. Meneruskan permintaan ke https://example.com.
  2. Menerima tanggapan dari https://example.com.
  3. Menambahkan Access-Control-Allow-Originheader ke respons.
  4. Lewati respons itu, dengan header tambahan itu, kembali ke kode frontend yang meminta.

Browser kemudian memungkinkan kode frontend untuk mengakses respons, karena respons dengan Access-Control-Allow-Originheader respons itulah yang dilihat browser.

Anda dapat dengan mudah menjalankan proxy Anda sendiri menggunakan kode dari https://github.com/Rob--W/cors-anywhere/ .
Anda juga dapat dengan mudah menggunakan proksi Anda sendiri ke Heroku hanya dalam 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-perintah itu, Anda akan berakhir dengan server CORS Anywhere Anda berjalan di, misalnya, 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; mis., https://cryptic-headland-94862.herokuapp.com/https://example.com .

Jadi jika ketika Anda mencoba menggunakan https://cors-anywhere.herokuapp.com, Anda merasa sedang down (yang kadang-kadang akan terjadi), maka pertimbangkan untuk mendapatkan akun Heroku (jika belum) dan ambil 2 atau 3 menit untuk melakukan langkah-langkah di atas untuk menggunakan server CORS Anywhere Anda sendiri di Heroku.

Terlepas dari itu, apakah Anda menjalankan sendiri atau menggunakan https://cors-anywhere.herokuapp.com atau proxy terbuka lainnya, solusi ini berfungsi bahkan jika permintaannya adalah yang memicu browser untuk melakukan OPTIONSpermintaan preflight CORS — karena dalam kasus itu, proksi juga mengirimkan kembali Access-Control-Allow-Headersdan Access-Control-Allow-Methodsheader yang diperlukan untuk membuat preflight berhasil.


Cara menghindari preflight CORS

Kode dalam pertanyaan memicu preflight CORS — karena mengirimkan Authorizationheader.

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

Bahkan tanpa itu, Content-Type: application/jsontajuk juga akan memicu preflight.

Apa "preflight" artinya: sebelum browser mencoba POSTkode dalam pertanyaan, pertama-tama ia akan mengirim OPTIONSpermintaan ke server - untuk menentukan apakah server memilih ikut menerima lintas asal POSTyang menyertakan Authorizationdan Content-Type: application/jsonheader.

Ini bekerja cukup baik dengan skrip ikal kecil - saya mendapatkan data saya.

Untuk mengujinya dengan benar curl, Anda harus meniru OPTIONSpermintaan preflight yang dikirimkan browser:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

... dengan https://the.sign_in.urldiganti dengan apa pun sign_inURL Anda yang sebenarnya .

Respons yang perlu dilihat browser dari OPTIONSpermintaan itu harus menyertakan tajuk seperti ini:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

Jika OPTIONSresponsnya tidak menyertakan tajuk tersebut, maka browser akan berhenti di sana dan bahkan tidak pernah mencoba mengirim POSTpermintaan. Selain itu, kode status HTTP untuk respons harus berupa 2xx — biasanya 200 atau 204. Jika ada kode status lain, peramban akan berhenti di sana.

Server dalam pertanyaan merespons OPTIONSpermintaan dengan kode status 501, yang tampaknya berarti berusaha menunjukkan tidak mengimplementasikan dukungan untuk OPTIONSpermintaan. Server lain biasanya merespons dengan kode status "Metode tidak diizinkan" 405 dalam kasus ini.

Jadi Anda tidak akan pernah dapat membuat POSTpermintaan langsung ke server dari kode JavaScript frontend Anda jika server merespons OPTIONSpermintaan itu dengan 405 atau 501 atau apa pun selain 200 atau 204 atau jika tidak menanggapi dengan yang diperlukan header respons.

Cara untuk menghindari memicu preflight untuk kasus dalam pertanyaan adalah:

  • jika server tidak memerlukan Authorizationtajuk permintaan tetapi sebagai gantinya (misalnya) mengandalkan data autentikasi yang disematkan di badan POSTpermintaan atau sebagai parameter kueri
  • jika server tidak meminta POSTtubuh untuk memiliki Content-Type: application/jsonjenis media, tetapi sebaliknya menerima POSTtubuh sebagai application/x-www-form-urlencodeddengan parameter bernama json(atau apa pun) yang nilainya adalah data JSON

Cara untuk memperbaiki “Access-Control-Allow-Origin sundulan tidak harus menjadi wildcard” masalah

Saya mendapatkan pesan kesalahan lain:

Nilai tajuk 'Akses-Kontrol-Bolehkan-Asal' dalam respons tidak boleh berupa wildcard '*' ketika mode kredensial permintaan adalah 'sertakan'. Origin ' http://127.0.0.1:3000 ' karenanya tidak diperbolehkan mengakses. Mode kredensial permintaan yang dimulai oleh XMLHttpRequest dikendalikan oleh atribut withCredentials.

Untuk permintaan yang menyertakan kredensial, browser tidak akan membiarkan kode JavaScript frontend Anda mengakses respons jika nilai Access-Control-Allow-Originheader responsnya *. Sebaliknya nilai dalam kasus itu harus sama persis dengan asal kode frontend Anda http://127.0.0.1:3000,.

Lihat permintaan terredensial dan wildcard di artikel MDN HTTP access control (CORS).

Jika Anda mengontrol server yang Anda kirimi permintaan, maka cara umum untuk menangani kasus ini adalah mengonfigurasi server untuk mengambil nilai Originheader permintaan, dan mengulanginya kembali ke nilai Access-Control-Allow-Originheader respons. Misalnya, dengan nginx:

add_header Access-Control-Allow-Origin $http_origin

Tapi itu hanya satu contoh; sistem server (web) lainnya menyediakan cara yang serupa untuk mengumandangkan nilai asal.


Saya menggunakan Chrome. Saya juga mencoba menggunakan Plugin Chrome CORS itu

Plugin Chrome CORS itu tampaknya hanya menyuntikkan Access-Control-Allow-Origin: *header secara sederhana ke respons yang dilihat browser. Jika plugin lebih pintar, apa yang akan dilakukannya adalah mengatur nilai Access-Control-Allow-Originheader respons palsu itu ke asal sebenarnya kode JavaScript frontend Anda http://127.0.0.1:3000,.

Jadi hindari menggunakan plugin itu, bahkan untuk pengujian. Itu hanya gangguan. Jika Anda ingin menguji tanggapan apa yang Anda dapatkan dari server tanpa peramban menyaringnya, Anda lebih baik menggunakan curl -Hseperti di atas.


Sejauh kode JavaScript frontend untuk fetch(…)permintaan dalam pertanyaan:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Hapus garis itu. The Access-Control-Allow-*header respon header. Anda tidak pernah ingin mengirim mereka dalam permintaan. Satu-satunya efek yang akan dimiliki adalah memicu browser untuk melakukan preflight.

sontonbarker
sumber
1
Jadi url akan terlihat seperti: 127.0.0.1:9999/127.0.0.1-05000 dengan cors, kan?
daniel.lozynski
Jika Anda menjalankan instance Anda sendiri dari github.com/Rob--W/cors-anywhere code di 127.0.0.1:9999 dan Anda ingin mengajukan permintaan ke 127.0.0.1.1000000 , ya
sontonbarker
4
Jawaban luar biasa, masalah saya adalah server jarak jauh tidak menanggapi permintaan OPSI, jadi setelah mengutak-atik permintaan dan tajuk untuk waktu yang sepertinya sudah saya selesaikan dengan menghapus tajuk Content-Typedan Access-Control-Allow-Origin- terima kasih!
Morvael
1
Saya tidak suka gagasan menggunakan proxy kecuali jika itu yang Anda kontrol, jika tidak Anda mengambil data melalui pihak ketiga yang tidak dipercaya dan terbuka untuk gangguan, dll.
DaveMongoose
1
Itu adalah jawaban yang mendalam dan luar biasa terima kasih, Apa yang saya lakukan secara pribadi ketika pengujian terbuka chrome dengan menonaktifkan keamanan web-flag dan itu berhasil. open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Karun
101

Kesalahan ini terjadi ketika URL klien dan URL server tidak cocok, termasuk nomor port. Dalam hal ini Anda perlu mengaktifkan layanan Anda untuk CORS yang merupakan pembagian sumber daya lintas asal.

Jika Anda meng-hosting layanan Spring REST maka Anda dapat menemukannya di posting blog dukungan CORS di Spring Framework .

Jika Anda meng-hosting layanan menggunakan server Node.js, maka

  1. Hentikan server Node.js.
  2. npm install cors --save
  3. Tambahkan baris berikut ke server.js Anda

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration
Rakesh
sumber
4
Bekerja tanpa masalah ... sangat membantu dengan tautan sumber daya pegas .. Terima kasih banyak
Rajesh Mbm
4
including the port number:(
scottysseus
1
Benar-benar membantu kamu telah membuat hariku
kunal sobat
nomor port menyelamatkan hidup saya
ashad
Jawaban paling sederhana yang pernah terima kasih :)
tumpukan
47

Masalah muncul karena Anda menambahkan kode berikut sebagai header permintaan di front-end Anda:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Header itu milik respons , bukan permintaan. Jadi hapus , termasuk baris:

headers.append('GET', 'POST', 'OPTIONS');

Permintaan Anda memiliki 'Content-Type: application / json', karenanya memicu apa yang disebut CORS preflight. Ini menyebabkan browser mengirim permintaan dengan metode OPTIONS. Lihat CORS preflight untuk informasi terperinci.
Karenanya di back-end Anda, Anda harus menangani permintaan yang sudah disorot ini dengan mengembalikan header respons yang meliputi:

Access-Control-Allow-Origin : http://localhost:3000
Access-Control-Allow-Credentials : true
Access-Control-Allow-Methods : GET, POST, OPTIONS
Access-Control-Allow-Headers : Origin, Content-Type, Accept

Tentu saja, sintaksis sebenarnya tergantung pada bahasa pemrograman yang Anda gunakan untuk back-end Anda.

Di front-end Anda, harus seperti ini:

function performSignIn() {
    let headers = new Headers();

    headers.append('Content-Type', 'application/json');
    headers.append('Accept', 'application/json');
    headers.append('Authorization', 'Basic ' + base64.encode(username + ":" +  password));
    headers.append('Origin','http://localhost:3000');

    fetch(sign_in, {
        mode: 'cors',
        credentials: 'include',
        method: 'POST',
        headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}
Lex Soft
sumber
4
Ini harus menjadi jawaban teratas - Saya benar-benar tidak menyukai gagasan melewati CORS, terutama dengan merutekannya melalui pihak ketiga.
DaveMongoose
hei, apa 'Header ()' tolong?
mitsu
@mitsu Jika Anda merujuk ke baris: let headers = new Headers (); di atas, maka itu adalah antarmuka ambil API untuk melakukan tindakan dengan permintaan http atau header tanggapan. Kunjungi developer.mozilla.org/en-US/docs/Web/API/Headers untuk mendapatkan detail serta contoh tentang cara menggunakannya.
Lex Soft
1
Jawaban yang jelas dan tepat .. logis dan persis mengatasi masalah ini. - Thx
Dhammika
7

Dalam kasus saya, saya menggunakan solusi di bawah ini

Front-end atau Angular

post(
    this.serverUrl, dataObjToPost,
    {
      headers: new HttpHeaders({
           'Content-Type':  'application/json',
         })
    }
)

back-end (saya menggunakan php)

header("Access-Control-Allow-Origin: http://localhost:4200");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header("Access-Control-Allow-Headers: Content-Type, Authorization");

$postdata = file_get_contents("php://input");
$request = json_decode($postdata);
print_r($request);
Harrison O
sumber
2

Hanya dua sen saya ... tentang Cara menggunakan proxy CORS untuk mengatasi masalah "Tidak ada Access-Control-Allow-Originheader"

Bagi Anda yang bekerja dengan php di backend, menggunakan "CORS proxy" sesederhana:

  1. buat file bernama 'no-cors.php' dengan konten berikut:

    $URL = $_GET['url']; echo json_encode(file_get_contents($URL)); die();

  2. di ujung depan Anda, lakukan sesuatu seperti:

    fetch('https://example.com/no-cors.php' + '?url=' + url) .then(response=>{*/Handle Response/*})

Yair Levy
sumber
1

Pindahkan ini:

credentials: 'include',
Nalan Madheswaran
sumber
1

Menggunakan dataType: 'jsonp'bekerja untuk saya.

   async function get_ajax_data(){
       var _reprojected_lat_lng = await $.ajax({
                                type: 'GET',
                                dataType: 'jsonp',
                                data: {},
                                url: _reprojection_url,
                                error: function (jqXHR, textStatus, errorThrown) {
                                    console.log(jqXHR)
                                },
                                success: function (data) {
                                    console.log(data);

                                    // note: data is already json type, you
                                    //       just specify dataType: jsonp
                                    return data;
                                }
                            });


 } // function               
hoogw
sumber
1

Dalam kasus saya, server web mencegah metode "PILIHAN"

Periksa server web Anda untuk metode opsi

Saya menggunakan "webtier"

/www/webtier/domains/[domainname[/config/fmwconfig/components/OHS/VCWeb1/httpd.conf

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

mengubah

<IfModule mod_rewrite.c>
  RewriteEngine off
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>
Juneho Nam
sumber
0

Saya bekerja dengan Spring REST, dan saya menyelesaikannya dengan menambahkan DiizinkanMetode ke dalam WebMvcConfigurer.

@Value( "${app.allow.origins}" )
    private String allowOrigins;    
@Bean
public WebMvcConfigurer corsConfigurer() {
            System.out.println("allow origin: "+allowOrigins);
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**")
                    //.allowedOrigins("http://localhost")
                    .allowedOrigins(allowOrigins)
                    .allowedMethods("PUT", "DELETE","GET", "POST");
                }
            };
        }
Jorge Dominguez
sumber
-1

Saya mendapatkan kesalahan ini di lokal saya. Saya menjalankan Visual Studio sebagai administrator dan itu diselesaikan.

ugurrrrr
sumber