javascript - Ingen 'Access-Control-Allow-Origin'-header finns på den begärda resursen—när du försöker hämta data från ett REST API


Jag försöker hämta lite data från REST API:t för HP Alm. Det fungerar ganska bra med ett litet curl-skript - jag får mina data.

Att göra det nu med JavaScript, fetch och ES6 (mer eller mindre) verkar vara ett större problem. Jag får hela tiden detta felmeddelande:

Fetch API kan inte ladda. Svar på förfrågan om förhandskontroll passerar inte åtkomstkontrollkontrollen: Ingen 'Access-Control-Allow-Origin'-header finns på den begärda resursen. Ursprung 'http://127.0.0.1:3000' får därför inte åtkomst. Svaret hade HTTP-statuskod 501. Om ett ogenomskinligt svar uppfyller dina behov, ställ in förfrågans läge till 'no-cors' för att hämta resursen med CORS inaktiverat.

Jag förstår att detta beror på att jag försöker hämta den datan från min localhost och lösningen bör vara att använda Cross-Origin Resource Sharing (CORS). Jag trodde faktiskt att jag gjorde det, men på något sätt ignorerar det antingen vad jag skriver i headern eller så är problemet något annat.

Så, finns det ett problem med implementeringen? Gör jag fel? Jag kan tyvärr inte kolla serverloggarna. Jag är verkligen lite fast här.

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

Jag använder Chrome. Jag försökte också använda det där Chrome CORS-pluginet, men då får jag ett annat felmeddelande:

Värdet på rubriken 'Access-Control-Allow-Origin' i svaret får inte vara wildcard '*' när begärans autentiseringsläge är 'include'. Ursprung 'http://127.0.0.1:3000' får därför inte åtkomst. Autentiseringsläget för begäranden som initieras av XMLHttpRequest styrs av attributet withCredentials.

Svar #1

Det här svaret täcker mycket, så det är uppdelat i tre delar:

  • Hur man använder en CORS-proxy för att undvika “No Access-Control-Allow-Origin header ”-problem
  • Hur man undviker CORS-förhandskontrollen
  • Hur man åtgärdar “Access-Control-Allow-Origin header får inte vara wildcard ”-problem

Hur man använder en CORS-proxy för att undvika „No Access-Control-Allow-Origin header”-problem

Om du inte kontrollerar servern som din frontend-kod skickar en förfrågan till, och problemet med svaret från den servern bara är bristen på den nödvändiga Access-Control-Allow-Origin-rubriken, kan du ändå få saker att fungera—genom att göra förfrågan via en CORS-proxy.

Du kan enkelt köra din egen proxy med koden från https://github.com/Rob--W/cors-anywhere/.
Du kan också enkelt distribuera din egen proxy till Heroku på bara 2-3 minuter, med 5 kommandon:

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

Efter att ha kört de kommandona kommer du att ha din egen CORS Anywhere-server som körs på, t.ex., https://cryptic-headland-94862.herokuapp.com/.

Nu, sätt prefixet på din begärans URL med URL:en för din proxy:

https://cryptic-headland-94862.herokuapp.com/https://example.com

Att lägga till proxy-URL:en som ett prefix gör att förfrågan skickas via din proxy, vilket:

  1. Vidarebefordrar begäran till https://example.com.
  2. Tar emot svaret från https://example.com.
  3. Lägger till Access-Control-Allow-Origin-huvudet i svaret.
  4. Skickar det svaret, med den tillagda rubriken, tillbaka till den begärande frontend-koden.

Webbläsaren tillåter sedan frontend-koden att få tillgång till svaret, eftersom det svaret med svarshuvudet Access-Control-Allow-Origin är vad webbläsaren ser.

Detta fungerar även om förfrågan är en som får webbläsare att göra en CORS-förhandskontroll OPTIONS-förfrågan, eftersom proxyn i så fall också skickar Access-Control-Allow-Headers och Access-Control-Allow-Methods-huvuden som behövs för att förhandskontrollen ska lyckas.


Hur man undviker CORS-förhandskontrollen

Koden i frågan utlöser en CORS-förflygning—eftersom den skickar en Authorization-rubrik.

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

Även utan det kommer Content-Type: application/json-headern också att utlösa en preflight.

Vad "preflight" betyder: innan webbläsaren försöker med POST i koden i frågan, skickar den först en OPTIONS-förfrågan till servern, för att avgöra om servern accepterar att ta emot en cross-origin POST som har Authorization och Content-Type: application/json-headers.

Det fungerar ganska bra med ett litet curl-skript - jag får mina data.

För att testa korrekt med curl måste du efterlikna de preflight OPTIONS som webbläsaren skickar:

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"

…med https://the.sign_in.url ersatt av den faktiska sign_in-URL:en du har.

Svaret som webbläsaren behöver från den OPTIONS-förfrågan måste ha rubriker som dessa:

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

Om OPTIONS-svaret inte inkluderar dessa headers, kommer webbläsaren att stanna precis där och aldrig försöka skicka POST-förfrågan. Dessutom måste HTTP-statuskoden för svaret vara en 2xx—vanligtvis 200 eller 204. Om det är någon annan statuskod kommer webbläsaren att stanna precis där.

Servern i fråga svarar på OPTIONS-förfrågan med en statuskod 501, vilket tydligen betyder att den försöker indikera att den inte implementerar stöd för OPTIONS-förfrågningar. Andra servrar svarar vanligtvis med en 405 "Method not allowed"-statuskod i detta fall.

Så du kommer aldrig kunna göra POST-förfrågningar direkt till den servern från din frontend JavaScript-kod om servern svarar på den OPTIONS-förfrågan med en 405 eller 501 eller något annat än en 200 eller 204, eller om den inte svarar med de nödvändiga svarshuvudena.

Sättet att undvika att utlösa en förhandskontroll för fallet i frågan skulle vara:

  • om servern inte krävde en Authorization-begäranheader utan istället, t.ex., förlitade sig på autentiseringsdata inbäddade i kroppen av POST-begäran eller som en frågeparameter
  • om servern inte krävde att POST-kroppen hade en Content-Type: application/json-mediatyp utan istället accepterade POST-kroppen som application/x-www-form-urlencoded med en parameter som heter json (eller vad som helst) vars värde är JSON-data

Hur man fixar „Access-Control-Allow-Origin header får inte vara wildcard”-problem

Jag får ett annat felmeddelande:

Värdet på rubriken 'Access-Control-Allow-Origin' i svaret får inte vara jokertecknet '*' när begärans autentiseringsläge är 'include'. Ursprung 'http://127.0.0.1:3000' är därför inte tillåten åtkomst. Autentiseringsläget för begäranden som initieras av XMLHttpRequest styrs av attributet withCredentials.

För förfrågningar som har autentiseringsuppgifter tillåter inte webbläsare att din frontend JavaScript-kod får åtkomst till svaret om värdet på Access-Control-Allow-Origin-huvudet är *. I stället måste värdet i det fallet exakt matcha din frontend-kods ursprung, http://127.0.0.1:3000.

Se Begäranden med autentisering och jokertecken i MDN-artikeln om HTTP åtkomstkontroll (CORS).

Om du kontrollerar servern som du skickar förfrågan till, är ett vanligt sätt att hantera detta fall att konfigurera servern att ta värdet från Origin-förfrågningshuvudet och återge/reflektera det tillbaka i värdet på Access-Control-Allow-Origin-svarshuvudet; till exempel med nginx:

add_header Access-Control-Allow-Origin $http_origin

Men det är bara ett exempel; andra (webb)serversystem har liknande sätt att återge ursprungsvärden.


Jag använder Chrome. Jag har också försökt använda det där Chrome CORS-pluginet

Den Chrome CORS-pluginen verkar tydligen bara enkelspårigt injicera en Access-Control-Allow-Origin: *-header i svaret som webbläsaren ser. Om pluginen vore smartare, skulle den göra så att värdet på den falska Access-Control-Allow-Origin-svarshuvudet sätts till den faktiska källan för din frontend JavaScript-kod, http://127.0.0.1:3000.

Så undvik att använda det där tillägget, även för testning. Det är bara en distraktion. För att testa vilka svar du får från servern utan att en webbläsare filtrerar dem, är det bättre att använda curl -H som ovan.


När det gäller frontend JavaScript-koden för fetch(…)-förfrågan i frågan:

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

Ta bort raderna ovan. Access-Control-Allow-*-huvuden är respons-huvuden. Du vill aldrig skicka dem i förfrågningar. Den enda effekten av det är att få en webbläsare att utföra en förhandskontroll.

Svar #2

Detta fel uppstår när klient-URL:en och server-URL:en inte matchar, inklusive portnumret. I detta fall behöver du aktivera din tjänst för CORS, vilket är delning av resurser mellan olika ursprung.

Om du hostar en Spring REST-tjänst kan du hitta den i blogginlägget CORS-stöd i Spring Framework.

Om du driver en tjänst med en Node.js-server då

  1. Stoppa Node.js-servern.
  2. npm install cors --save
  3. Lägg till följande rader i din server.js
const cors=require("cors");
const corsOptions ={
   origin:'*', 
   credentials:true,            //access-control-allow-credentials:true
   optionSuccessStatus:200,
}

app.use(cors(corsOptions)) // Use this after the variable declaration

Svar #3

Problemet uppstod eftersom du lade till följande kod som request-header i din front-end:

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

De där rubrikerna tillhör svaret, inte förfrågan. Så ta bort dem, inklusive raden:

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

Din begäran hade 'Content-Type: application/json', vilket därför utlöste det som kallas CORS-förflyttning. Detta gjorde att webbläsaren skickade begäran med OPTIONS-metoden. Se CORS-förflyttning för detaljerad information.

Därför måste du i din back-end hantera denna preflight-förfrågan genom att returnera svarshuvuden som inkluderar:

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

Självklart beror den faktiska syntaxen på vilket programspråk du använder för din back-end.

I din front-end bör det vara så här:

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

Svar #4

I mitt fall använder jag lösningen nedan.

Front-end eller Angular

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

back-end (jag använder 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);

Svar #5

Att lägga till mode:no-cors kan undvika CORS-problem i API:et.

Dock kan det vara osäkert i produktion, eftersom det i princip inaktiverar skyddsfunktioner mot korsdomäner på klienterna.


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('Failed: ' + error.message));

Svar #6

Om ditt API är skrivet i ASP.NET Core, följ då stegen nedan:

  • Installera Microsoft.AspNetCore.Cors-paketet.

  • Lägg till följande rad i metoden ConfigureServices i filen Startup.cs:

    services.AddCors();
    
  • Lägg till raden nedan i Configure-metoden i filen startup.cs:

    app.UseCors(options =>
         options.WithOrigins("http://localhost:8080")
                .AllowAnyHeader()
                .AllowAnyMethod());
    
  • Se till att du lägger till detta efter - app.UseRouting();

    Se bilden nedan (från MSDN) för att se mellanprogrammens ordning:

    https://i.sstatic.net/vQ4yT.png

Svar #7

Att använda dataType: 'jsonp' fungerade för mig.

   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               

Relaterade artiklar