Versuch, Fetch und Pass im Modus zu verwenden: no-cors

164

Ich kann diesen Endpunkt http://catfacts-api.appspot.com/api/facts?number=99über Postman erreichen und er kehrt zurückJSON

Zusätzlich verwende ich die Create-React-App und möchte vermeiden, dass eine Serverkonfiguration eingerichtet wird.

In meinem Client-Code versuche fetchich, dasselbe zu tun, aber ich erhalte die Fehlermeldung:

In der angeforderten Ressource ist kein Header 'Access-Control-Allow-Origin' vorhanden. Origin ' http: // localhost: 3000 ' ist daher kein Zugriff gestattet. Wenn eine undurchsichtige Antwort Ihren Anforderungen entspricht, setzen Sie den Anforderungsmodus auf "no-cors", um die Ressource mit deaktiviertem CORS abzurufen.

Also versuche ich, ein Objekt an meinen Fetch zu übergeben, wodurch CORS wie folgt deaktiviert wird:

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

Interessanterweise ist der Fehler, den ich bekomme, tatsächlich ein Syntaxfehler mit dieser Funktion. Ich bin nicht sicher, ob mein fetchObjekt defekt ist, denn wenn ich das Objekt {mode: 'no-cors'} entferne und es mit einer anderen URL versorge, funktioniert es einwandfrei.

Ich habe auch versucht, das Objekt zu übergeben { mode: 'opaque'}, aber dies gibt den ursprünglichen Fehler von oben zurück.

Ich glaube, alles was ich tun muss, ist CORS zu deaktivieren. Was fehlt mir?

dwww
quelle

Antworten:

287

mode: 'no-cors'wird die Dinge nicht auf magische Weise zum Laufen bringen. Tatsächlich macht es die Sache noch schlimmer, weil ein Effekt darin besteht, den Browsern zu sagen: "Blockiere meinen Frontend-JavaScript-Code unter allen Umständen daran, den Inhalt des Antwortkörpers und der Header anzuzeigen." Natürlich willst du das fast nie.

Was bei Ursprungsübergreifenden Anforderungen von Frontend-JavaScript passiert, ist, dass Browser standardmäßig den Frontend-Code für den grenzüberschreitenden Zugriff auf Ressourcen blockieren. Wenn Access-Control-Allow-Origineine Antwort vorliegt, lockern die Browser diese Blockierung und ermöglichen Ihrem Code den Zugriff auf die Antwort.

Wenn eine Site jedoch keine Access-Control-Allow-OriginAntworten sendet , kann Ihr Frontend-Code nicht direkt auf die Antworten dieser Site zugreifen. Insbesondere können Sie das Problem nicht durch Angabe beheben mode: 'no-cors'(dies stellt sicher , dass Ihr Frontend-Code nicht auf den Antwortinhalt zugreifen kann).

Eines wird jedoch funktionieren: Wenn Sie Ihre Anfrage über einen CORS-Proxy wie folgt senden :

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>

Hinweis: Wenn Sie versuchen, https://cors-anywhere.herokuapp.com zu verwenden, stellen Sie fest, dass es nicht funktioniert . Sie können Ihren eigenen Proxy auch einfach in nur 2-3 Minuten mit 5 Befehlen für Heroku bereitstellen:

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

Nachdem Sie diese Befehle ausgeführt haben, wird Ihr eigener CORS Anywhere-Server ausgeführt, z . B. https://cryptic-headland-94862.herokuapp.com/ . Stellen Sie Ihrer Anforderungs-URL also https://cors-anywhere.herokuapp.comstatt der URL die URL für Ihre eigene Instanz voran. Beispiel: https://cryptic-headland-94862.herokuapp.com/https://example.com .


Ich kann diesen Endpunkt http://catfacts-api.appspot.com/api/facts?number=99über Postman erreichen

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS erklärt, warum Sie mit Postman nicht über das Frontend auf die Antwort zugreifen können, obwohl Sie mit Postman auf die Antwort zugreifen können JavaScript-Code, der in einer Webanwendung ausgeführt wird, es sei denn, die Antwort enthält einen Access-Control-Allow-OriginAntwortheader.

http://catfacts-api.appspot.com/api/facts?number=99 hat keinen Access-Control-Allow-OriginAntwortheader, sodass Ihr Frontend-Code nicht auf den Antwortursprung zugreifen kann.

Ihr Browser kann die Antwort gut erhalten und Sie können sie in Postman und sogar in Browser-Devtools sehen - aber das bedeutet nicht, dass Browser sie Ihrem Code aussetzen. Sie werden nicht, weil es keinen Access-Control-Allow-OriginAntwortheader hat. Sie müssen stattdessen einen Proxy verwenden, um ihn abzurufen.

Der Proxy sendet die Anforderung an diese Site, erhält die Antwort, fügt den Access-Control-Allow-OriginAntwortheader und alle anderen benötigten CORS-Header hinzu und gibt diese an Ihren anfordernden Code zurück. Und diese Antwort mit dem Access-Control-Allow-Originhinzugefügten Header wird vom Browser angezeigt, sodass der Browser Ihren Frontend-Code tatsächlich auf die Antwort zugreifen lässt.


Also versuche ich, ein Objekt an meinen Fetch zu übergeben, wodurch CORS deaktiviert wird

Das willst du nicht. Um klar zu sein, wenn Sie sagen, dass Sie CORS deaktivieren möchten, meinen Sie tatsächlich, dass Sie die Richtlinie mit demselben Ursprung deaktivieren möchten . CORS selbst ist tatsächlich ein Weg, dies zu tun - CORS ist ein Weg, die Richtlinie mit demselben Ursprung zu lockern, nicht ein Weg, sie einzuschränken.

Es ist jedoch richtig, dass Sie - nur in Ihrer lokalen Umgebung - beispielsweise Ihrem Browser Laufzeitflags geben können, um die Sicherheit zu deaktivieren und unsicher auszuführen, oder Sie können eine Browsererweiterung lokal installieren, um die Richtlinie mit demselben Ursprung zu umgehen ist die Situation nur für Sie vor Ort zu ändern.

Unabhängig davon, was Sie lokal ändern, wird jeder andere, der versucht, Ihre App zu verwenden, weiterhin auf die Richtlinie mit demselben Ursprung stoßen, und Sie können dies auf keinen Fall für andere Benutzer Ihrer App deaktivieren.

Sie möchten es höchstwahrscheinlich nie mode: 'no-cors'in der Praxis anwenden, außer in einigen wenigen Fällen , und selbst dann nur, wenn Sie genau wissen, was Sie tun und welche Auswirkungen dies hat. Das liegt daran, dass die Einstellung mode: 'no-cors'dem Browser tatsächlich sagt: "Blockiere meinen Frontend-JavaScript-Code unter allen Umständen daran, den Inhalt des Antworttextes und der Header zu untersuchen." In den meisten Fällen ist das offensichtlich nicht das, was Sie wollen.


Informationen zu den Fällen, in denen Sie die Verwendung in Betracht ziehen mode: 'no-cors', finden Sie in der Antwort unter Welche Einschränkungen gelten für undurchsichtige Antworten? für die Details. Der Kern davon ist, dass die Fälle sind:

  • In dem begrenzten Fall , wenn Sie JavaScript verwenden eine Ressource von einer anderen Herkunft , den Inhalt eines zu machen <script>, <link rel=stylesheet>, <img>, <video>, <audio>, <object>, <embed>, oder <iframe>Elements (die wegen Einbettung von Ressourcen funktionieren Cross-Origin ist für diejenigen erlaubt) - aber aus irgendeinem Grunde Sie möchten oder können dies nicht tun, indem Sie beim Markup des Dokuments die Ressourcen-URL als hrefoder- srcAttribut für das Element verwenden.

  • Wenn das einzige, was Sie mit einer Ressource tun möchten, das Zwischenspeichern ist. Wie in der Antwort unter Welche Einschränkungen gelten für undurchsichtige Antworten? In der Praxis gilt das Szenario für die Verwendung von Service Workern. In diesem Fall ist die relevante API die Cache-Speicher-API .

Aber auch in diesen begrenzten Fällen sind einige wichtige Fallstricke zu beachten. Siehe die Antwort unter Welche Einschränkungen gelten für undurchsichtige Antworten? für die Details.


Ich habe auch versucht, das Objekt weiterzugeben { mode: 'opaque'}

Es gibt kein mode: 'opaque' Anforderungsmodus - opaqueist stattdessen nur eine Eigenschaft der Antwort , und Browser legen diese undurchsichtige Eigenschaft für Antworten von Anforderungen fest, die mit dem no-corsModus gesendet wurden .

Übrigens ist das Wort undurchsichtig ein ziemlich explizites Signal für die Art der Antwort, die Sie erhalten: „undurchsichtig“ bedeutet, dass Sie es nicht sehen können.

Sideshowbarker
quelle
4
Ich cors-anywheremag die Problemumgehung für einfache Anwendungsfälle ohne Produkt (dh das Abrufen einiger öffentlich verfügbarer Daten). Diese Antwort bestätigt meinen Verdacht, der no-corsnicht häufig ist, da seine undurchsichtige Antwort nicht sehr nützlich ist. dh "sehr begrenzte Fälle"; Kann mir jemand Beispiele erklären, wo no-corses nützlich ist?
Die rote Erbse
1
@TheRedPea Siehe das Update ich hier auf die Antwort aus (und dass ich auch als Kommentare für Ihre Frage an hinzugefügt stackoverflow.com/questions/52569895/... )
sideshowbarker
Ich musste meinen Express-Server mit dem CORS.js-Paket - github.com/expressjs/cors - konfigurieren und dann aus der Abrufanforderung entfernen mode: 'no-cors'(andernfalls wäre die Antwort leer)
James L.
Der CORS-Proxy ist großartig. Ich bin erstaunt, dass dies als "Sicherheitsmaßnahme" angesehen wird, um den Zugriff auf öffentliche Dateien überhaupt zu blockieren. Aus Hacker-Sicht ist es so einfach, sich fortzubewegen, und genau das macht es. Es ist wirklich nur ein Ärger für gute Schauspieler.
Seph Reed
Ich generiere den Code verschiedener Clients, die dieselbe API verwenden. Ich benutze es für Schulungen. Ich möchte den Code einfach halten und die Benutzer damit spielen lassen. Ich muss No-Cros verwenden.
profimedica
6

Wenn Sie also wie ich eine Website auf localhost entwickeln, auf der Sie versuchen, Daten von der Laravel-API abzurufen und in Ihrem Vue-Frontend zu verwenden, und Sie dieses Problem sehen, habe ich es folgendermaßen gelöst:

  1. Führen Sie in Ihrem Laravel-Projekt den Befehl aus php artisan make:middleware Cors. Dies wird app/Http/Middleware/Cors.phpfür Sie erstellen .
  2. Fügen Sie den folgenden Code in die handlesFunktion ein 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.phpden folgenden Eintrag im $routeMiddlewareArray:

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

    (Es würde andere Einträge in dem Array wie auth, guestetc. Auch stellen Sie sicher , du bist dies zu tun , app/Http/kernel.phpweil es eine andere kernel.phpzu in Laravel)

  4. Fügen Sie diese Middleware bei der Routenregistrierung für alle Routen hinzu, auf die Sie den Zugriff zulassen möchten, wie folgt:

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
  5. Stellen Sie im Vue-Frontend sicher, dass Sie diese API in mounted()Funktion und nicht in aufrufen data(). Stellen Sie außerdem sicher, dass Sie http://oder https://mit der URL in Ihrem fetch()Anruf verwenden.

Volle Credits für Pete Houstons Blog-Artikel .

Punkt net
quelle
3

Die einfache Lösung: Fügen Sie ganz oben in der PHP-Datei, von der Sie die Daten anfordern, Folgendes hinzu.

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

Wirklich schöner Code
quelle
6
Dies ist eine sehr gute Idee, wenn Sie die geringstmögliche Sicherheit wünschen :).
Heretic Monkey
Was ist mit Bild-Hotlink
user889030
1

Die Lösung für mich war, es einfach serverseitig zu machen

Ich habe die C # WebClient-Bibliothek verwendet, um die Daten (in meinem Fall Bilddaten) abzurufen und an den Client zurückzusenden. Es gibt wahrscheinlich etwas sehr Ähnliches in Ihrer gewählten serverseitigen Sprache.

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

Sie können es an Ihren eigenen Anwendungsfall anpassen. Der Hauptpunkt istclient.DownloadData() ohne CORS-Fehler bearbeitet. In der Regel treten CORS-Probleme nur zwischen Websites auf. Daher ist es in Ordnung, standortübergreifende Anforderungen von Ihrem Server aus zu stellen.

Dann ist der React Fetch-Aufruf so einfach wie:

//React component

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

       // Do more stuff....
    )}
Stuart Aitken
quelle
1

Sehr einfache Lösung (2 Minuten zu konfigurieren) ist die Verwendung von local-ssl-proxy package fromnpm

Die Verwendung ist ziemlich einfach:
1. Installieren Sie das Paket: npm install -g local-ssl-proxy
2. Während Sie Ihre local-serverMaske mit dem ausführen local-ssl-proxy --source 9001 --target 9000

PS: Ersetzen Sie --target 9000mit der -- "number of your port"und --source 9001mit--source "number of your port +1"

volna
quelle
1
Ich habe Probleme bei der Verwendung meiner App in einem echten Telefon. Ist Ihre Lösung für diesen Fall hilfreich?
Hamid Araghi
@ HamidAraghi Ich würde annehmen, dass jetzt, da für die Entwicklungszwecke der Proxyserver auf dem Gerät laufen sollte, das tatsächlich von dem Fehler betroffen ist
Volna