Prova di usare fetch e pass in modalità: no-cors


167

Posso raggiungere questo endpoint, http://catfacts-api.appspot.com/api/facts?number=99tramite Postman e ritornaJSON

Inoltre sto usando creare-reagire-app e vorrei evitare di configurare qualsiasi configurazione del server.

Nel mio codice client sto cercando di usare fetchper fare la stessa cosa, ma ottengo l'errore:

Nessuna intestazione 'Access-Control-Allow-Origin' è presente sulla risorsa richiesta. L'origine ' http: // localhost: 3000 ' non è quindi consentito l'accesso. Se una risposta opaca soddisfa le tue esigenze, imposta la modalità della richiesta su "no-cors" per recuperare la risorsa con CORS disabilitato.

Quindi sto cercando di passare un oggetto al mio Fetch che disabiliterà CORS, in questo modo:

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

È interessante notare che l'errore che ottengo è in realtà un errore di sintassi con questa funzione. Non sono sicuro che il mio effettivo fetchsia rotto, perché quando rimuovo l'oggetto {mode: 'no-cors'} e gli fornisco un URL diverso, funziona perfettamente.

Ho anche provato a passare l'oggetto { mode: 'opaque'}, ma questo restituisce l'errore originale dall'alto.

Credo che tutto ciò che devo fare è disabilitare CORS .. Cosa mi sto perdendo?

Risposte:


292

mode: 'no-cors'non farà magicamente far funzionare le cose. In effetti, le cose peggiorano, perché un effetto che ha è quello di dire ai browser: "Impedisci al mio codice JavaScript front-end di guardare i contenuti del corpo della risposta e delle intestazioni in ogni circostanza". Ovviamente non lo vuoi quasi mai.

Ciò che accade con le richieste di origine incrociata da JavaScript frontend è che i browser per impostazione predefinita impediscono al codice frontend di accedere alle risorse di origine incrociata. Se Access-Control-Allow-Originè presente una risposta, i browser rilasseranno tale blocco e consentiranno al codice di accedere alla risposta.

Ma se un sito invia no Access-Control-Allow-Originnelle sue risposte, il tuo codice frontend non può accedere direttamente alle risposte da quel sito. In particolare, non è possibile risolverlo specificando mode: 'no-cors'(in realtà ciò garantirà che il codice frontend non possa accedere ai contenuti della risposta).

Tuttavia, una cosa che sarà lavorare: se si invia una richiesta tramite un proxy CORS , in questo modo:

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>

Nota: se quando vai a provare a utilizzare https://cors-anywhere.herokuapp.com, trovi che è inattivo , 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, anziché aggiungere il prefisso all'URL della richiesta https://cors-anywhere.herokuapp.com, prefisso invece l'URL per la tua istanza; ad esempio, https://cryptic-headland-94862.herokuapp.com/https://example.com .


Posso raggiungere questo endpoint, http://catfacts-api.appspot.com/api/facts?number=99tramite Postman

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS spiega perché, nonostante sia possibile accedere alla risposta con Postman, i browser non ti consentono di accedere alla risposta tra origini dal frontend Codice JavaScript in esecuzione in un'app Web a meno che la risposta non includa Access-Control-Allow-Originun'intestazione di risposta.

http://catfacts-api.appspot.com/api/facts?number=99 non ha Access-Control-Allow-Originun'intestazione di risposta, quindi non è possibile che il tuo codice frontend possa accedere all'origine incrociata della risposta.

Il tuo browser può ottenere la risposta bene e puoi vederlo in Postman e persino negli sviluppatori del browser, ma ciò non significa che i browser lo esporranno al tuo codice. Non lo faranno, perché non ha Access-Control-Allow-Originun'intestazione di risposta. Quindi devi invece usare un proxy per ottenerlo.

Il proxy effettua la richiesta a quel sito, ottiene la risposta, aggiunge l' Access-Control-Allow-Originintestazione della risposta e qualsiasi altra intestazione CORS necessaria, quindi la restituisce al codice richiedente. E quella risposta con l' Access-Control-Allow-Originintestazione aggiunta è ciò che vede il browser, quindi il browser consente al tuo codice frontend di accedere effettivamente alla risposta.


Quindi sto cercando di passare un oggetto al mio Fetch che disabiliterà CORS

Non vuoi farlo. Per essere chiari, quando dici di voler "disabilitare CORS" sembra che tu voglia effettivamente disabilitare la stessa politica di origine . Lo stesso CORS è in realtà un modo per farlo - CORS è un modo per allentare la stessa politica di origine, non un modo per limitarla.

Tuttavia, è vero che puoi - solo nel tuo ambiente locale - fare cose come dare ai flag di runtime del browser per disabilitare la sicurezza ed eseguire in modo non sicuro, oppure puoi installare un'estensione del browser localmente per aggirare la stessa politica di origine, ma tutto ciò che fa è cambiare la situazione solo per te a livello locale.

Indipendentemente da ciò che cambi localmente, chiunque cerchi di utilizzare la tua app continuerà a seguire la stessa politica di origine e non è possibile disabilitarlo per gli altri utenti della tua app.

Molto probabilmente non vorrai mai usare mode: 'no-cors'in pratica, tranne in alcuni casi limitati , e anche solo allora saprai esattamente cosa stai facendo e quali sono gli effetti. Questo perché l'impostazione mode: 'no-cors'che dice effettivamente al browser è: "Impedisci al mio codice JavaScript front-end di esaminare il contenuto del corpo della risposta e le intestazioni in ogni circostanza". Nella maggior parte dei casi, ovviamente, non è proprio quello che vuoi.


Per quanto riguarda i casi in cui vorresti prendere in considerazione l'utilizzo mode: 'no-cors', vedi la risposta in Quali limiti si applicano alle risposte opache? per i dettagli. L'essenza di ciò è che i casi sono:

  • Nel caso limitata quando si sta utilizzando Javascript per mettere i contenuti da un altro l'origine in una <script>, <link rel=stylesheet>, <img>, <video>, <audio>, <object>, <embed>, o <iframe>elemento (che funziona perché l'incasso di risorse: Cross-origine è consentito per quelli) - ma per qualche ragione si don' non voglio o non posso farlo semplicemente facendo in modo che il markup del documento utilizzi l'URL della risorsa come attributo hrefo srcper l'elemento.

  • Quando l'unica cosa che vuoi fare con una risorsa è memorizzarla nella cache. Come indicato nella risposta Quali limiti si applicano alle risposte opache? , in pratica lo scenario a cui si applica è quando si utilizzano Service Workers, nel qual caso l'API pertinente è l' API di archiviazione cache .

Ma anche in quei casi limitati, ci sono alcuni aspetti importanti di cui tenere conto; vedere la risposta in Quali limitazioni si applicano alle risposte opache? per i dettagli.


Ho anche provato a passare l'oggetto { mode: 'opaque'}

Non esiste una mode: 'opaque'modalità di richiesta: opaqueè invece solo una proprietà della risposta e i browser impostano quella proprietà opaca sulle risposte dalle richieste inviate con la no-corsmodalità.

Ma per inciso la parola opaco è un segnale piuttosto esplicito sulla natura della risposta con cui si finisce: "opaco" significa che non è possibile vederlo.


4
Adoro la cors-anywheresoluzione alternativa per semplici casi d'uso non-prod (ad esempio recuperare alcuni dati disponibili al pubblico). Questa risposta conferma il mio sospetto che no-corsnon è comune perché la sua OpaqueResponse non è molto utile; vale a dire "casi molto limitati"; qualcuno può spiegarmi esempi dove no-corsè utile?
The Red Pea,

1
@TheRedPea Vedi l'aggiornamento che ho apportato alla risposta qui (e che ho anche aggiunto come commenti per la tua domanda su stackoverflow.com/questions/52569895/… )
sideshowbarker

Avevo bisogno di configurare il mio server Express con il pacchetto CORS.js - github.com/expressjs/cors - e quindi dovevo rimuovere mode: 'no-cors'dalla richiesta di recupero (altrimenti la risposta sarebbe vuota)
James L.

il proxy CORS è fantastico. Sono sorpreso che sia considerata una misura di "sicurezza" per bloccare l'accesso ai file pubblici. È così, così facile spostarsi dal punto di vista degli hacker, ed è esattamente quello che fa. È davvero solo una seccatura per buoni attori.
Seph Reed,

Genero il codice di diversi client che consumano la stessa API. Lo uso per gli allenamenti. Voglio mantenere il codice semplice e consentire agli utenti di giocarci. Devo usare no-cros.
profimedica,

6

Quindi, se sei come me e stai sviluppando un sito Web su localhost in cui stai provando a recuperare i dati dall'API Laravel e utilizzarli nel tuo front-end Vue e vedi questo problema, ecco come l'ho risolto:

  1. Nel tuo progetto Laravel, esegui il comando php artisan make:middleware Cors. Questo creerà app/Http/Middleware/Cors.phpper te.
  2. Aggiungi il seguente codice all'interno della handlesfunzione in Cors.php:

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  3. In app/Http/kernel.php, aggiungi la seguente voce $routeMiddlewarenell'array:

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

    (Ci sarebbero altre voci nell'array come auth, guestecc. Assicurati anche di farlo app/Http/kernel.phpperché ce ne è un'altra kernel.phpanche in Laravel)

  4. Aggiungi questo middleware alla registrazione del percorso per tutti i percorsi a cui desideri consentire l'accesso, in questo modo:

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
  5. Nel front-end Vue, assicurati di chiamare questa API in mounted()funzione e non in data(). Assicurati anche di utilizzare http://o https://con l'URL nella tua fetch()chiamata.

Crediti completi per l' articolo del blog di Pete Houston .


2

La soluzione semplice: aggiungi quanto segue all'inizio del file php da cui stai richiedendo i dati.

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


8
Questa è un'ottima idea se si desidera la minima quantità possibile di sicurezza :).
Heretic Monkey,

che dire immagine hotlink
user889030

1

La soluzione per me era di farlo sul lato server

Ho usato la WebClientlibreria C # per ottenere i dati (nel mio caso erano dati di immagine) e rispedirli al client. Probabilmente c'è qualcosa di molto simile nella lingua scelta sul lato server.

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

Puoi modificarlo su qualunque sia il tuo caso d'uso. Il punto principale viene client.DownloadData()lavorato senza errori CORS. In genere i problemi CORS si verificano solo tra i siti Web, quindi va bene fare richieste "tra siti" dal tuo server.

Quindi la chiamata di recupero React è semplice come:

//React component

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

       // Do more stuff....
    )}

1

Una soluzione molto semplice (2 minuti per la configurazione) è usare il pacchetto local-ssl-proxy danpm

L'utilizzo è piuttosto semplice:
1. Installa il pacchetto: npm install -g local-ssl-proxy
2. Mentre esegui la local-servermaschera con illocal-ssl-proxy --source 9001 --target 9000

PS: sostituisci --target 9000con -- "number of your port"e --source 9001con--source "number of your port +1"


1
Ho problemi a utilizzare la mia app nel dispositivo telefonico reale. La tua soluzione è utile per questo caso?
Hamid Araghi,

@HamidAraghi Suppongo che ora, poiché per scopi di sviluppo il server proxy dovrebbe essere in esecuzione sul dispositivo che è effettivamente interessato dall'errore
volna,
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.