Nessuna intestazione "Access-Control-Allow-Origin" è presente sulla risorsa richiesta, quando si tenta di ottenere dati da un'API REST


469

Sto cercando di recuperare alcuni dati dall'API REST di HP Alm. Funziona abbastanza bene con un piccolo script di arricciatura: ottengo i miei dati.

Ora farlo con JavaScript, fetch ed ES6 (più o meno) sembra essere un problema più grande. Continuo a ricevere questo messaggio di errore:

Impossibile recuperare l'API di recupero. La risposta alla richiesta di verifica preliminare non supera il controllo del controllo di accesso: non è presente alcuna intestazione "Access-Control-Allow-Origin" sulla risorsa richiesta. L'origine ' http://127.0.0.1:3000 ' non è quindi consentito l'accesso. La risposta aveva il codice di stato HTTP 501. Se una risposta opaca soddisfa le tue esigenze, imposta la modalità della richiesta su "no-cors" per recuperare la risorsa con CORS disabilitato.

Capisco che questo è perché sto cercando di recuperare quei dati dal mio localhost e la soluzione dovrebbe usare CORS. Ora pensavo di averlo effettivamente fatto, ma in qualche modo ignora ciò che scrivo nell'intestazione o il problema è qualcos'altro?

Quindi, c'è un problema di implementazione? Sto sbagliando? Purtroppo non riesco a controllare i log del server. Sono davvero un po 'bloccato qui.

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

Sto usando Chrome. Ho anche provato a utilizzare quel plug-in Chrome CORS, ma poi ricevo un altro messaggio di errore:

Il valore dell'intestazione 'Access-Control-Allow-Origin' nella risposta non deve essere il carattere jolly '*' quando la modalità credenziali della richiesta è 'include'. L'origine ' http://127.0.0.1:3000 ' non è quindi consentito l'accesso. La modalità credenziali delle richieste avviate da XMLHttpRequest è controllata dall'attributo withCredentials.

Risposte:


826

Questa risposta copre molto terreno, quindi è divisa in tre parti:

  • Come utilizzare un proxy CORS per aggirare i problemi di "Nessuna intestazione Access-Control-Allow-Origin"
  • Come evitare il preflight CORS
  • Come risolvere i problemi "L'intestazione Access-Control-Allow-Origin non deve essere il jolly"

Come utilizzare un proxy CORS per aggirare i problemi di "Nessuna intestazione Access-Control-Allow-Origin"

Se non controlli il server a cui il tuo codice JavaScript front-end sta inviando una richiesta e il problema con la risposta da quel server è solo la mancanza dell'intestazione necessaria Access-Control-Allow-Origin, puoi comunque far funzionare le cose, facendo la richiesta attraverso un Proxy CORS. Per mostrare come funziona, innanzitutto ecco un codice che non utilizza un 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?"))

Il motivo per cui il catchblocco viene colpito lì è che il browser impedisce a quel codice di accedere alla risposta da cui ritorna https://example.com. E il motivo per cui il browser lo fa è che la risposta manca Access-Control-Allow-Origindell'intestazione della risposta.

Ora, ecco esattamente lo stesso esempio, ma solo con un proxy CORS aggiunto in:

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?"))

Nota: se https://cors-anywhere.herokuapp.com è inattivo o non disponibile quando lo provi, vedi sotto per come distribuire il tuo server CORS Anywhere su Heroku in soli 2-3 minuti.

Il secondo frammento di codice sopra riportato può accedere correttamente alla risposta perché prendendo l'URL della richiesta e cambiandolo in https://cors-anywhere.herokuapp.com/https://example.com —solo semplicemente prefissandolo con l'URL del proxy — provoca il richiesta di esecuzione tramite tale proxy, che quindi:

  1. Inoltra la richiesta a https://example.com.
  2. Riceve la risposta da https://example.com.
  3. Aggiunge l' Access-Control-Allow-Originintestazione alla risposta.
  4. Passa quella risposta, con quell'intestazione aggiunta, al codice frontend richiedente.

Il browser quindi consente al codice frontend di accedere alla risposta, poiché quella risposta con l' Access-Control-Allow-Originintestazione della risposta è ciò che il browser vede.

Puoi facilmente eseguire il tuo proxy usando il codice da https://github.com/Rob--W/cors-anywhere/ .
Puoi anche distribuire facilmente il tuo proxy su Heroku in letteralmente solo 2-3 minuti, con 5 comandi:

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

Dopo aver eseguito questi comandi, finirai con il tuo server CORS Anywhere in esecuzione su, ad esempio, https://cryptic-headland-94862.herokuapp.com/ . Quindi, invece di aggiungere il prefisso all'URL della tua richiesta https://cors-anywhere.herokuapp.com, aggiungi come prefisso l'URL della tua istanza; ad esempio, https://cryptic-headland-94862.herokuapp.com/https://example.com .

Quindi se quando vai a provare a usare https://cors-anywhere.herokuapp.com, trovi che è inattivo (come a volte lo sarà), quindi considera di ottenere un account Heroku (se non lo hai già fatto) e prendi 2 o 3 minuti per eseguire i passaggi precedenti per distribuire il proprio server CORS Anywhere su Heroku.

Indipendentemente dal fatto che tu esegua il tuo o usi https://cors-anywhere.herokuapp.com o altri proxy aperti, questa soluzione funziona anche se la richiesta è quella che innesca i browser a fare una OPTIONSrichiesta di preflight CORS , perché in quel caso, il il proxy restituisce anche le intestazioni Access-Control-Allow-Headerse Access-Control-Allow-Methodsnecessarie per far funzionare il preflight.


Come evitare il preflight CORS

Il codice nella domanda attiva un preflight CORS, poiché invia Authorizationun'intestazione.

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

Anche senza quello, anche l' Content-Type: application/jsonintestazione avrebbe innescato il preflight.

Che cosa significa "preflight": prima che il browser provi il POSTcodice nella domanda, invierà prima una OPTIONSrichiesta al server - per determinare se il server sta optando per la ricezione di un'origine incrociata POSTche include le intestazioni Authorizatione Content-Type: application/json.

Funziona abbastanza bene con un piccolo script di arricciatura: ottengo i miei dati.

Per testare correttamente curl, è necessario emulare la OPTIONSrichiesta di verifica preliminare che il browser invia:

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"

... con https://the.sign_in.urlsostituito da qualunque sia il tuo sign_inURL reale .

La risposta che il browser deve vedere da quella OPTIONSrichiesta deve includere intestazioni come questa:

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

Se la OPTIONSrisposta non include queste intestazioni, il browser si fermerà proprio lì e non tenterà nemmeno di inviare la POSTrichiesta. Inoltre, il codice di stato HTTP per la risposta deve essere un 2xx, in genere 200 o 204. Se è un altro codice di stato, il browser si fermerà proprio lì.

Il server nella domanda sta rispondendo alla OPTIONSrichiesta con un codice di stato 501, il che apparentemente significa che sta cercando di indicare che non implementa il supporto per le OPTIONSrichieste. In genere, altri server rispondono con un codice di stato 405 "Metodo non consentito".

Quindi non sarai mai in grado di fare POSTrichieste direttamente a quel server dal tuo codice JavaScript front-end se il server risponde a quella OPTIONSrichiesta con un 405 o 501 o qualcosa di diverso da un 200 o 204 o se non risponde con quelli necessari intestazioni di risposta.

Il modo per evitare di innescare un preflight per il caso nella domanda sarebbe:

  • se il server non richiedeva Authorizationun'intestazione della richiesta ma invece (ad esempio) si basava su dati di autenticazione incorporati nel corpo della POSTrichiesta o come parametro di query
  • se il server non ha richiesto al POSTcorpo di avere un Content-Type: application/jsontipo di supporto ma invece ha accettato il POSTcorpo come application/x-www-form-urlencodedcon un parametro chiamato json(o qualunque altra cosa) il cui valore è i dati JSON

Come risolvere i problemi "L'intestazione Access-Control-Allow-Origin non deve essere il jolly"

Ricevo un altro messaggio di errore:

Il valore dell'intestazione 'Access-Control-Allow-Origin' nella risposta non deve essere il carattere jolly '*' quando la modalità credenziali della richiesta è 'include'. L'origine ' http://127.0.0.1:3000 ' non è quindi consentito l'accesso. La modalità credenziali delle richieste avviate da XMLHttpRequest è controllata dall'attributo withCredentials.

Per una richiesta che include credenziali, i browser non consentiranno al codice JavaScript front-end di accedere alla risposta se il valore dell'intestazione della Access-Control-Allow-Originrisposta è *. Invece il valore in quel caso deve corrispondere esattamente l'origine del codice di frontend, http://127.0.0.1:3000.

Vedere Richieste e caratteri jolly con credenziali nell'articolo MDS HTTP access control (CORS).

Se controlli il server a cui stai inviando la richiesta, un modo comune per gestire questo caso è configurare il server in modo che prenda il valore dell'intestazione della Originrichiesta e rispecchi / rifletta tale valore nel valore dell'intestazione della Access-Control-Allow-Originrisposta. Ad esempio, con nginx:

add_header Access-Control-Allow-Origin $http_origin

Ma questo è solo un esempio; altri sistemi server (web) forniscono modi simili per echo valori di origine.


Sto usando Chrome. Ho anche provato a utilizzare quel plug-in Chrome CORS

Quel plug-in Chrome CORS apparentemente sembra semplicemente Access-Control-Allow-Origin: *inserire un'intestazione nella risposta che il browser vede. Se il plugin fosse più intelligente, quello che avrebbe fatto è impostare il valore di tale falsa Access-Control-Allow-Originintestazione di risposta alla vera origine del frontend codice JavaScript, http://127.0.0.1:3000.

Quindi evita di usare quel plugin, anche per i test. È solo una distrazione. Se vuoi testare le risposte che ricevi dal server senza che il browser le filtri, è meglio usare curl -Hcome sopra.


Per quanto riguarda il codice JavaScript frontend per la fetch(…)richiesta nella domanda:

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

Rimuovi quelle righe. Le Access-Control-Allow-*intestazioni sono intestazioni di risposta . Non vuoi mai inviarli in una richiesta. L'unico effetto che avrà è di innescare un browser per eseguire un preflight.


1
Quindi l'URL sarebbe simile a: 127.0.0.1:9999/127.0.0.1:5000 con cors, giusto?
daniel.lozynski,

Se stai eseguendo la tua istanza del codice github.com/Rob--W/cors-anywhere al 127.0.0.1:9999 e vuoi fare una richiesta al 127.0.0.1:5000 , sì
sideshowbarker

4
Risposta superba, il mio problema era che il server remoto non rispondeva alle richieste OPTIONS, quindi dopo aver armeggiato con richieste e intestazioni per quelle che sembravano secoli ho risolto rimuovendo le intestazioni Content-Typee Access-Control-Allow-Origin- grazie!
Morvael,

1
Non mi piace l'idea di utilizzare un proxy a meno che non sia uno che controlli, altrimenti stai recuperando i dati tramite una terza parte non attendibile ed è aperto a manomissioni, ecc.
DaveMongoose,

1
Questa è una risposta approfondita e superba grazie, quello che ho fatto personalmente durante il test è stato Open Chrome con flag di disabilitazione del web e ha funzionato. open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Karun,

101

Questo errore si verifica quando l'URL del client e l'URL del server non corrispondono, incluso il numero di porta. In questo caso è necessario abilitare il servizio per CORS che è la condivisione delle risorse tra le origini.

Se stai ospitando un servizio Spring REST, puoi trovarlo nel post del blog Supporto CORS in Spring Framework .

Se stai ospitando un servizio utilizzando un server Node.js, allora

  1. Arrestare il server Node.js.
  2. npm install cors --save
  3. Aggiungi le seguenti righe al tuo server.js

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration

4
Ha funzionato senza problemi ... molto utile con il collegamento delle risorse di primavera .. Grazie mille
Rajesh Mbm,

4
including the port number:(
scottysseus

1
Davvero utile, hai reso la mia giornata
amico di Kunal il

numero di porta ha salvato la vita
Ashad

La risposta più semplice mai grazie :)
impila

47

Il problema è sorto perché hai aggiunto il seguente codice come intestazione della richiesta nel tuo front-end:

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

Queste intestazioni appartengono alla risposta , non alla richiesta. Quindi rimuovili , inclusa la linea:

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

La tua richiesta aveva "Content-Type: application / json", quindi ha innescato quello che viene chiamato preflight CORS. Ciò ha causato la richiesta inviata dal browser con il metodo OPTIONS. Vedere Preflight CORS per informazioni dettagliate.
Pertanto, nel back-end , è necessario gestire questa richiesta preflight restituendo le intestazioni di risposta che includono:

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

Naturalmente, la sintassi effettiva dipende dal linguaggio di programmazione utilizzato per il back-end.

Nel tuo front-end, dovrebbe essere così:

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

4
Questa dovrebbe essere la risposta migliore: non mi piace l'idea di bypassare CORS, in particolare instradandolo attraverso una terza parte.
DaveMongoose,

ehi, che cos'è "Header ()" per favore?
mitsu,

@mitsu Se fai riferimento alla riga: let headers = new Headers (); sopra, quindi è un'interfaccia dell'API di recupero per eseguire azioni con le richieste HTTP o le intestazioni di risposta. Visita developer.mozilla.org/en-US/docs/Web/API/Headers per ottenere i dettagli e gli esempi su come utilizzarlo.
Lex Soft

1
Risposta chiara e precisa .. è logica e risolve esattamente il problema. - Thx
Dhammika,

7

Nel mio caso, utilizzo la soluzione di seguito

Front-end o angolare

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

back-end (utilizzo 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);

2

Solo i miei due centesimi ... per quanto riguarda come utilizzare un proxy CORS per aggirare i Access-Control-Allow-Originproblemi di "Nessuna intestazione"

Per quelli di voi che lavorano con php nel backend, implementare un "proxy CORS" è semplice come:

  1. crea un file chiamato 'no-cors.php' con il seguente contenuto:

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

  2. sul tuo front-end, fai qualcosa del tipo:

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



1

Usando ha dataType: 'jsonp'funzionato per me.

   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               

1

Nel mio caso, il web server ha impedito il metodo "OPTIONS"

Controlla il tuo server web per il metodo delle opzioni

Sto usando "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>

cambia in

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

0

Stavo lavorando con Spring REST, e l'ho risolto aggiungendo i AllowMethods in 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");
                }
            };
        }

-1

Stavo ricevendo questo errore sul mio locale. Ho eseguito Visual Studio come amministratore e si è risolto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.