In der angeforderten Ressource ist kein Header 'Access-Control-Allow-Origin' vorhanden, wenn versucht wird, Daten von einer REST-API abzurufen

465

Ich versuche, einige Daten von der REST-API von HP Alm abzurufen. Es funktioniert ziemlich gut mit einem kleinen Curl-Skript - ich bekomme meine Daten.

Jetzt scheint dies mit JavaScript, Fetch und ES6 (mehr oder weniger) ein größeres Problem zu sein. Ich erhalte immer wieder die folgende Fehlermeldung:

Die Abruf-API kann nicht geladen werden. Die Antwort auf die Preflight-Anforderung besteht die Zugriffssteuerungsprüfung nicht: In der angeforderten Ressource ist kein Header 'Access-Control-Allow-Origin' vorhanden. Origin ' http://127.0.0.1:3000 ' ist daher kein Zugriff gestattet. Die Antwort hatte den HTTP-Statuscode 501. Wenn eine undurchsichtige Antwort Ihren Anforderungen entspricht, setzen Sie den Anforderungsmodus auf "no-cors", um die Ressource mit deaktiviertem CORS abzurufen.

Ich verstehe, dass dies daran liegt, dass ich versuche, diese Daten aus meinem lokalen Host abzurufen, und die Lösung sollte CORS verwenden. Jetzt dachte ich, ich hätte das tatsächlich getan, aber irgendwie ignoriert es entweder, was ich in den Header schreibe, oder das Problem ist etwas anderes?

Gibt es also ein Implementierungsproblem? Mache ich es falsch Ich kann die Serverprotokolle leider nicht überprüfen. Ich stecke hier wirklich ein bisschen fest.

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

Ich benutze Chrome. Ich habe auch versucht, dieses Chrome CORS-Plugin zu verwenden, aber dann wird eine weitere Fehlermeldung angezeigt:

Der Wert des Headers 'Access-Control-Allow-Origin' in der Antwort darf nicht der Platzhalter '*' sein, wenn der Anmeldeinformationsmodus der Anforderung 'include' ist. Origin ' http://127.0.0.1:3000 ' ist daher kein Zugriff gestattet. Der Anmeldeinformationsmodus von Anforderungen, die von XMLHttpRequest initiiert wurden, wird durch das Attribut withCredentials gesteuert.

daniel.lozynski
quelle

Antworten:

822

Diese Antwort ist sehr umfangreich und besteht aus drei Teilen:

  • So verwenden Sie einen CORS-Proxy, um Probleme mit dem Header "Keine Zugriffssteuerung, Zulassen des Ursprungs" zu umgehen
  • So vermeiden Sie den CORS-Preflight
  • So beheben Sie das Problem "Access-Control-Allow-Origin-Header darf nicht der Platzhalter sein"

So verwenden Sie einen CORS-Proxy, um Probleme mit dem Header "Keine Zugriffssteuerung, Zulassen des Ursprungs" zu umgehen

Wenn Sie den Server nicht steuern, an den Ihr Frontend-JavaScript-Code eine Anfrage sendet, und das Problem mit der Antwort von diesem Server nur das Fehlen des erforderlichen Access-Control-Allow-OriginHeaders ist, können Sie die Dinge trotzdem zum Laufen bringen - indem Sie die Anfrage über a stellen CORS-Proxy. Um zu zeigen, wie das funktioniert, folgt zunächst ein Code, der keinen CORS-Proxy verwendet:

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

Der Grund, warum der catchBlock dort getroffen wird, ist, dass der Browser verhindert, dass dieser Code auf die Antwort zugreift, von der er zurückkommt https://example.com. Der Grund dafür ist, dass der Antwort der Antwortheader fehlt Access-Control-Allow-Origin.

Hier ist genau das gleiche Beispiel, jedoch nur mit einem hinzugefügten CORS-Proxy:

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

Hinweis: Wenn https://cors-anywhere.herokuapp.com nicht verfügbar oder nicht verfügbar ist, wenn Sie es versuchen, lesen Sie unten, wie Sie Ihren eigenen CORS Anywhere-Server bei Heroku in nur 2-3 Minuten bereitstellen.

Das zweite Code-Snippet oben kann erfolgreich auf die Antwort zugreifen, da die Verwendung der Anforderungs-URL in https://cors-anywhere.herokuapp.com/https://example.com - indem nur die Proxy-URL vorangestellt wird - die Antwort verursacht Anfrage, über diesen Proxy gemacht zu werden, der dann:

  1. Leitet die Anfrage an weiter https://example.com.
  2. Erhält die Antwort von https://example.com.
  3. Fügt Access-Control-Allow-Originder Antwort den Header hinzu.
  4. Übergibt diese Antwort mit dem hinzugefügten Header an den anfordernden Frontend-Code.

Der Browser ermöglicht dann dem Frontend-Code den Zugriff auf die Antwort, da diese Antwort mit dem Access-Control-Allow-OriginAntwortheader vom Browser angezeigt wird.

Sie können Ihren eigenen Proxy einfach mit Code von https://github.com/Rob--W/cors-anywhere/ ausführen .
Sie können auch einfach Ihren eigenen Proxy für Heroku in nur 2-3 Minuten mit 5 Befehlen 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 nicht https://cors-anywhere.herokuapp.comdie URL für Ihre eigene Instanz voran, anstatt sie mit einem Präfix zu versehen. Beispiel: https://cryptic-headland-94862.herokuapp.com/https://example.com .

Wenn Sie also versuchen, https://cors-anywhere.herokuapp.com zu verwenden, stellen Sie fest, dass es nicht verfügbar ist (was manchmal der Fall sein wird), sollten Sie ein Heroku-Konto einrichten (falls Sie dies noch nicht getan haben) und 2 nehmen oder 3 Minuten, um die oben genannten Schritte auszuführen und Ihren eigenen CORS Anywhere-Server auf Heroku bereitzustellen.

Unabhängig davon, ob Sie Ihre eigene ausführen oder https://cors-anywhere.herokuapp.com oder einen anderen offenen Proxy verwenden, funktioniert diese Lösung auch dann, wenn die Anforderung Browser dazu veranlasst, eine CORS-Preflight- OPTIONSAnforderung auszuführen. In diesem Fall ist die Der Proxy sendet auch die Access-Control-Allow-Headersund Access-Control-Allow-MethodsHeader zurück, die für den erfolgreichen Preflight erforderlich sind.


So vermeiden Sie den CORS-Preflight

Der Code in der Frage löst einen CORS-Preflight aus, da er einen AuthorizationHeader sendet .

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

Auch ohne das Content-Type: application/jsonwürde der Header auch den Preflight auslösen.

Was "Preflight" bedeutet: Bevor der Browser POSTden Code in der Frage versucht, sendet er zuerst eine OPTIONSAnfrage an den Server, um festzustellen, ob der Server sich dafür entscheidet, einen Cross-Origin zu erhalten POST, der die Header Authorizationund enthält Content-Type: application/json.

Es funktioniert ziemlich gut mit einem kleinen Curl-Skript - ich bekomme meine Daten.

Um ordnungsgemäß zu testen curl, müssen Sie die Preflight- OPTIONSAnforderung emulieren, die der Browser sendet:

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"

… Mit https://the.sign_in.urlersetzt durch was auch immer Ihre tatsächliche sign_inURL ist.

Die Antwort, die der Browser auf diese OPTIONSAnfrage sehen muss , muss folgende Header enthalten:

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

Wenn die OPTIONSAntwort diese Header nicht enthält, stoppt der Browser genau dort und versucht nicht einmal, die POSTAnfrage zu senden . Außerdem muss der HTTP-Statuscode für die Antwort 2xx sein - normalerweise 200 oder 204. Wenn es sich um einen anderen Statuscode handelt, stoppt der Browser genau dort.

Der Server in der Frage antwortet auf die OPTIONSAnfrage mit einem 501-Statuscode, was anscheinend bedeutet, dass er angibt, dass er keine Unterstützung für OPTIONSAnfragen implementiert . Andere Server antworten in diesem Fall normalerweise mit dem Statuscode 405 "Methode nicht zulässig".

Sie können also niemals POSTAnfragen von Ihrem Frontend-JavaScript-Code direkt an diesen Server stellen, wenn der Server auf diese OPTIONSAnfrage mit einem 405 oder 501 oder etwas anderem als 200 oder 204 antwortet oder wenn er nicht mit den erforderlichen Antworten antwortet Antwortheader.

Der Weg, um zu vermeiden, dass ein Preflight für den Fall in der Frage ausgelöst wird, wäre:

  • Wenn der Server keinen AuthorizationAnforderungsheader benötigte, sondern sich (zum Beispiel) auf Authentifizierungsdaten stützte, die im Hauptteil der POSTAnforderung oder als Abfrageparameter eingebettet waren
  • Wenn der Server nicht verlangt hat, dass der POSTBody einen Content-Type: application/jsonMedientyp hat, sondern den POSTBody wie application/x-www-form-urlencodedmit einem Parameter namens json(oder was auch immer) akzeptiert, dessen Wert die JSON-Daten sind

So beheben Sie das Problem "Access-Control-Allow-Origin-Header darf nicht der Platzhalter sein"

Ich erhalte eine weitere Fehlermeldung:

Der Wert des Headers 'Access-Control-Allow-Origin' in der Antwort darf nicht der Platzhalter '*' sein, wenn der Anmeldeinformationsmodus der Anforderung 'include' ist. Origin ' http://127.0.0.1:3000 ' ist daher kein Zugriff gestattet. Der Anmeldeinformationsmodus von Anforderungen, die von XMLHttpRequest initiiert wurden, wird durch das Attribut withCredentials gesteuert.

Bei einer Anfrage mit Anmeldeinformationen lassen Browser Ihren Frontend-JavaScript-Code nicht auf die Antwort zugreifen, wenn der Wert des Access-Control-Allow-OriginAntwortheaders lautet *. Stattdessen muss der Wert in diesem Fall genau mit dem Ursprung Ihres Frontend-Codes übereinstimmen http://127.0.0.1:3000.

Siehe Anforderungen und Platzhalter für Anmeldeinformationen im Artikel zur MDN-HTTP-Zugriffssteuerung (CORS).

Wenn Sie den Server steuern, an den Sie die Anforderung senden, können Sie diesen Fall häufig so konfigurieren, dass der Wert des OriginAnforderungsheaders übernommen und in den Wert des Access-Control-Allow-OriginAntwortheaders zurückgeführt wird. Zum Beispiel mit nginx:

add_header Access-Control-Allow-Origin $http_origin

Aber das ist nur ein Beispiel; Andere (Web-) Serversysteme bieten ähnliche Möglichkeiten, um Ursprungswerte wiederzugeben.


Ich benutze Chrome. Ich habe auch versucht, dieses Chrome CORS-Plugin zu verwenden

Dieses Chrome CORS-Plugin fügt anscheinend einfach einen Access-Control-Allow-Origin: *Header in die Antwort ein, die der Browser sieht. Wenn das Plugin intelligenter wäre, würde es den Wert dieses gefälschten Access-Control-Allow-OriginAntwortheaders auf den tatsächlichen Ursprung Ihres Frontend-JavaScript-Codes setzen http://127.0.0.1:3000.

Vermeiden Sie es daher, dieses Plugin auch zum Testen zu verwenden. Es ist nur eine Ablenkung. Wenn Sie testen möchten, welche Antworten Sie vom Server erhalten, ohne dass ein Browser sie filtert, sollten Sie curl -Hwie oben beschrieben verwenden.


Soweit der Frontend-JavaScript-Code für die fetch(…)Anfrage in der Frage:

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

Entfernen Sie diese Linien. Die Access-Control-Allow-*Header sind Antwort - Header. Sie möchten sie niemals in einer Anfrage senden. Der einzige Effekt besteht darin, einen Browser für einen Preflight auszulösen.

Sideshowbarker
quelle
1
Die URL würde also so aussehen: 127.0.0.1:9999/127.0.0.1:5000 mit cors, oder?
daniel.lozynski
Wenn Sie Ihre eigene Instanz des Codes github.com/Rob--W/cors-anywhere unter 127.0.0.1:9999 ausführen und eine Anfrage an 127.0.0.1:5000 stellen möchten , ja
Sideshowbarker
4
Hervorragende Antwort, mein Problem war, dass der Remote-Server nicht auf OPTIONS-Anfragen reagierte. Nachdem ich mich über lange Zeit mit Anfragen und Headern beschäftigt hatte, löste ich das Problem, indem ich die Header entfernte Content-Typeund Access-Control-Allow-Origin- danke!
Morvael
1
Ich mag die Idee, einen Proxy zu verwenden, nur, wenn Sie ihn kontrollieren. Andernfalls
rufen
1
Das ist eine ausführliche und hervorragende Antwort. Vielen Dank. Was ich persönlich beim Testen getan habe, war Open Chrome mit Disable-Web Security Flag und es hat funktioniert. open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Karun
101

Dieser Fehler tritt auf, wenn die Client-URL und die Server-URL einschließlich der Portnummer nicht übereinstimmen. In diesem Fall müssen Sie Ihren Dienst für CORS aktivieren, bei dem es sich um eine gemeinsame Nutzung von Ressourcen handelt.

Wenn Sie einen Spring REST-Service hosten, finden Sie ihn im CORS-Support des Blogposts in Spring Framework .

Wenn Sie einen Dienst über einen Node.js-Server hosten, dann

  1. Stoppen Sie den Node.js-Server.
  2. npm install cors --save
  3. Fügen Sie Ihrer server.js folgende Zeilen hinzu

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration
Rakesh
quelle
4
Arbeitete ohne Probleme ... sehr hilfreich mit Spring Resource Link .. Vielen Dank
Rajesh Mbm
4
including the port number:(
Scottysseus
1
Wirklich hilfreich du hast meinen Tag gemacht
kunal pal
Portnummer rettete mein Leben
Ashad
Einfachste Antwort aller Zeiten danke :)
Stapel
46

Das Problem trat auf, weil Sie den folgenden Code als Anforderungsheader in Ihrem Front-End hinzugefügt haben :

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

Diese Header gehören zur Antwort , nicht zur Anfrage. So entfernen Sie sie, einschließlich der Zeile:

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

Ihre Anfrage hatte 'Content-Type: application / json' und löste daher den sogenannten CORS-Preflight aus. Dies führte dazu, dass der Browser die Anforderung mit der OPTIONS-Methode sendete. Ausführliche Informationen finden Sie im CORS-Preflight .
Daher müssen Sie in Ihrem Back-End diese vorab geflogene Anforderung bearbeiten, indem Sie die Antwortheader zurückgeben, die Folgendes enthalten:

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

Die tatsächliche Syntax hängt natürlich von der Programmiersprache ab, die Sie für Ihr Back-End verwenden.

In Ihrem Frontend sollte es so sein:

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
quelle
4
Dies sollte die beste Antwort sein - ich mag die Idee, CORS zu umgehen, wirklich nicht, insbesondere indem ich sie durch einen Dritten weitergebe.
DaveMongoose
Hey, was ist 'Header ()' bitte?
Mitsu
@mitsu Wenn Sie sich auf die Zeile beziehen: let headers = new Headers (); oben ist es dann eine Schnittstelle der Abruf-API, um Aktionen mit http-Anforderungs- oder Antwortheadern auszuführen. Besuchen Sie developer.mozilla.org/en-US/docs/Web/API/Headers , um Details sowie Beispiele zur Verwendung zu erhalten.
Lex Soft
1
Klare und präzise Antwort. Es ist logisch und spricht das Problem genau an. - Thx
Dhammika
7

In meinem Fall verwende ich die folgende Lösung

Frontend oder Angular

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

Backend (ich benutze 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.
quelle
2

Nur meine zwei Cent ... bezüglich der Verwendung eines CORS-Proxys, um Access-Control-Allow-OriginProbleme ohne Header zu umgehen

Für diejenigen unter Ihnen, die mit PHP im Backend arbeiten, ist die Bereitstellung eines "CORS-Proxys" so einfach wie:

  1. Erstellen Sie eine Datei mit dem Namen 'no-cors.php' mit folgendem Inhalt:

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

  2. Machen Sie an Ihrem Frontend etwas wie:

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

Yair Levy
quelle
1

Entferne das:

credentials: 'include',
Nalan Madheswaran
quelle
1

Verwenden dataType: 'jsonp'hat bei mir funktioniert.

   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
quelle
1

In meinem Fall hat der Webserver die Methode "OPTIONS" verhindert

Überprüfen Sie Ihren Webserver auf die Optionsmethode

Ich benutze "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>

ändern

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

Ich habe mit Spring REST gearbeitet und es gelöst, indem ich die AllowedMethods zum WebMvcConfigurer hinzugefügt habe.

@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
quelle
-1

Ich habe diesen Fehler auf meinem lokalen erhalten. Ich habe Visual Studio als Administrator ausgeführt und es wurde behoben.

ugurrrrr
quelle