javascript - Ingen 'Access-Control-Allow-Origin'-header er til stede på den forespurte ressursen—når du prøver å hente data fra en REST API


Jeg prøver å hente noen data fra REST API-en til HP ALM. Det fungerer ganske bra med et lite curl-skript—jeg får dataene mine.

Å gjøre det nå med JavaScript, fetch og ES6 (mer eller mindre) ser ut til å være et større problem. Jeg får stadig denne feilmeldingen:

Fetch API kan ikke laste. Responsen på preflight-forespørselen passerer ikke tilgangskontrollsjekken: Ingen 'Access-Control-Allow-Origin'-header er tilstede på den forespurte ressursen. Opprinnelsen 'http://127.0.0.1:3000' har derfor ikke tilgang. Responsen hadde HTTP-statuskode 501. Hvis en utydelig respons dekker behovene dine, sett forespørselens modus til 'no-cors' for å hente ressursen med CORS deaktivert.

Jeg forstår at dette er fordi jeg prøver å hente de dataene fra min localhost, og løsningen bør være å bruke Cross-Origin Resource Sharing (CORS). Jeg trodde faktisk at jeg gjorde det, men på en eller annen måte enten ignorerer det det jeg skriver i headeren, eller problemet er noe annet.

Så, er det et implementeringsproblem? Gjør jeg det feil? Jeg kan dessverre ikke sjekke serverloggene. Jeg sitter virkelig litt fast her.

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

Jeg bruker Chrome. Jeg prøvde også å bruke den Chrome CORS-pluginen, men da får jeg en annen feilmelding:

Verdien av headeren 'Access-Control-Allow-Origin' i responsen må ikke være wildcard '*' når forespørselens credentials-modus er 'include'. Opprinnelse 'http://127.0.0.1:3000' har derfor ikke tilgang. Credentials-modusen til forespørsler startet av XMLHttpRequest kontrolleres av withCredentials-attributtet.

Svar #1

Dette svaret dekker mye, så det er delt inn i tre deler:

  • Hvordan bruke en CORS-proxy for å unngå “No Access-Control-Allow-Origin header”-problemer
  • Hvordan unngå CORS-forhåndskontroll
  • Hvordan fikse “Access-Control-Allow-Origin header må ikke være jokertegn” problemer

Hvordan bruke en CORS-proxy for å unngå “No Access-Control-Allow-Origin header” problemer

Hvis du ikke kontrollerer serveren frontend-koden din sender en forespørsel til, og problemet med svaret fra den serveren bare er mangelen på den nødvendige Access-Control-Allow-Origin-headeren, kan du fortsatt få ting til å fungere—ved å gjøre forespørselen gjennom en CORS-proxy.

Du kan enkelt kjøre din egen proxy med koden fra https://github.com/Rob--W/cors-anywhere/.
Du kan også enkelt distribuere din egen proxy til Heroku på bare 2-3 minutter, med 5 kommandoer:

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

Etter å ha kjørt disse kommandoene, vil du ende opp med din egen CORS Anywhere-server som kjører på, for eksempel, https://cryptic-headland-94862.herokuapp.com/.

Nå, legg til prefikset for din forespørsels-URL med URL-en for proxyen din:

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

Å legge til proxy-URL-en som et prefiks gjør at forespørselen blir sendt gjennom proxyen din, noe som:

  1. Viderekobler forespørselen til https://example.com.
  2. Mottar svaret fra https://example.com.
  3. Legger til Access-Control-Allow-Origin-headeren i responsen.
  4. Sender det svaret, med den ekstra headeren, tilbake til den forespørrende frontend-koden.

Nettleseren lar deretter frontend-koden få tilgang til svaret, fordi det svaret med Access-Control-Allow-Origin-responshodet er det nettleseren ser.

Dette fungerer selv om forespørselen er en som utløser at nettlesere gjør en CORS preflight OPTIONS-forespørsel, fordi proxyen i så fall også sender Access-Control-Allow-Headers og Access-Control-Allow-Methods headerne som trengs for at preflight skal lykkes.


Hvordan unngå CORS-forhåndskontrollen

Koden i spørsmålet utløser en CORS-forhåndsforespørsel siden den sender en Authorization-header.

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

Selv uten det vil Content-Type: application/json-headeren også utløse en preflight.

Hva "preflight" betyr: før nettleseren prøver POST i koden i spørsmålet, sender den først en OPTIONS-forespørsel til serveren, for å avgjøre om serveren godtar å motta en cross-origin POST som har Authorization og Content-Type: application/json-headere.

Det fungerer ganske bra med et lite curl-skript - jeg får dataene mine.

For å teste riktig med curl, må du etterligne forhåndsforespørselen OPTIONS som nettleseren sender:

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 erstattet av hva enn din faktiske sign_in-URL er.

Responsen nettleseren trenger fra den OPTIONS-forespørselen må ha overskrifter som dette:

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

Hvis OPTIONS-responsen ikke inkluderer disse headerne, vil nettleseren stoppe der og aldri forsøke å sende POST-forespørselen. I tillegg må HTTP-statuskoden for responsen være en 2xx—typisk 200 eller 204. Hvis det er en annen statuskode, vil nettleseren stoppe der.

Serveren det er snakk om svarer på OPTIONS-forespørselen med en 501-statuskode, som tilsynelatende betyr at den prøver å indikere at den ikke implementerer støtte for OPTIONS-forespørsler. Andre servere svarer vanligvis med en 405 «Method not allowed»-statuskode i dette tilfellet.

Så du vil aldri kunne sende POST-forespørsler direkte til den serveren fra frontend JavaScript-koden din hvis serveren svarer på den OPTIONS-forespørselen med en 405 eller 501 eller noe annet enn en 200 eller 204, eller hvis den ikke svarer med de nødvendige responshodene.

Måten å unngå å utløse en preflight for saken i spørsmålet ville være:

  • hvis serveren ikke krevde en Authorization-forespørselsheader, men i stedet for eksempel stolte på autentiseringsdata som var innebygd i kroppen av POST-forespørselen eller som en spørringsparameter
  • hvis serveren ikke krevde at POST-kroppen hadde medietypen Content-Type: application/json, men i stedet aksepterte POST-kroppen som application/x-www-form-urlencoded med en parameter kalt json (eller hva som helst) hvis verdi er JSON-dataene

Hvordan fikse “Access-Control-Allow-Origin header må ikke være jokertegnet” problemer

Jeg får en annen feilmelding:

Verdien av 'Access-Control-Allow-Origin'-headeren i svaret må ikke være jokertegn '*' når forespørslens credentials-modus er 'include'. Origin 'http://127.0.0.1:3000' er derfor ikke tillatt tilgang. Credentials-modusen for forespørsler initiert av XMLHttpRequest styres av withCredentials-attributtet.

For forespørsler som har legitimasjon, vil ikke nettlesere la frontend JavaScript-koden din få tilgang til responsen hvis verdien av Access-Control-Allow-Origin-headeren er *. I stedet må verdien i dette tilfellet nøyaktig samsvare med origin for frontend-koden din, http://127.0.0.1:3000.

Se Godkjente forespørsler og jokertegn i MDNs artikkel om HTTP-tilgangskontroll (CORS).

Hvis du kontrollerer serveren du sender forespørselen til, er en vanlig måte å håndtere dette tilfellet på å konfigurere serveren til å ta verdien av Origin-forespørselsheaderen, og gjenspeile/returnere den tilbake i verdien av Access-Control-Allow-Origin-responshodet; f.eks. med nginx:

add_header Access-Control-Allow-Origin $http_origin

Men det er bare et eksempel; andre (web) serversystemer har lignende måter å gjenspeile opprinnelsesverdier på.


Jeg bruker Chrome. Jeg prøvde også å bruke den Chrome CORS-pluginen

Den Chrome CORS-pluginen injiserer tilsynelatende bare ukomplisert en Access-Control-Allow-Origin: * header i responsen som nettleseren ser. Hvis pluginen var smartere, ville det den gjorde være å sette verdien av den falske Access-Control-Allow-Origin response-headeren til den faktiske opprinnelsen til frontend JavaScript-koden din, http://127.0.0.1:3000.

Så unngå å bruke den pluginen, selv for testing. Det er bare en distraksjon. For å teste hvilke svar du får fra serveren uten at en nettleser filtrerer dem, er det bedre å bruke curl -H som ovenfor.


Når det gjelder frontend JavaScript-koden for fetch(…)-forespørselen i spørsmålet:

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

Fjern linjene ovenfor. Access-Control-Allow-*-headerne er respons-headere. Du vil aldri sende dem i forespørsler. Den eneste effekten av det er å få en nettleser til å utføre en preflight.

Svar #2

Denne feilen oppstår når klient-URL-en og server-URL-en ikke samsvarer, inkludert portnummeret. I dette tilfellet må du aktivere tjenesten din for CORS, som er deling av ressurser på tvers av opprinnelse.

Hvis du er vert for en Spring REST-tjeneste, kan du finne den i blogginnlegget CORS-støtte i Spring Framework.

Hvis du driver en tjeneste ved hjelp av en Node.js-server, så

  1. Stopp Node.js-serveren.
  2. npm install cors --save
  3. Legg til følgende linjer i 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 oppsto fordi du la til følgende kode som request-header i front-end-en din:

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

Disse overskriftene tilhører responsen, ikke forespørselen. Så fjern dem, inkludert linjen:

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

Forespørselen din hadde 'Content-Type: application/json', og utløste dermed det som kalles CORS preflight. Dette fikk nettleseren til å sende forespørselen med OPTIONS-metoden. Se CORS preflight for detaljert informasjon.

Derfor må du i ditt backend håndtere denne forhåndsforespørselen ved å returnere responsheaderne som inkluderer:

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

Selvfølgelig avhenger den faktiske syntaksen av programmeringsspråket du bruker for back-end.

I front-end-en din bør det være slik:

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 tilfelle bruker jeg løsningen nedenfor.

Front-end eller Angular

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

backend (jeg bruker 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

Å legge til mode:no-cors kan unngå CORS-problemer i API-et.

Selv om det kan være usikkert i produksjon, siden det i praksis deaktiverer kryss-domenebeskyttelsesfunksjoner på klienter.


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

Hvis API-en din er skrevet i ASP.NET Core, vennligst følg trinnene nedenfor:

  • Installer Microsoft.AspNetCore.Cors-pakken.

  • Legg til følgende linje i ConfigureServices-metoden i filen Startup.cs:

    services.AddCors();
    
  • Legg til følgende linje i Configure-metoden i filen startup.cs:

    app.UseCors(options =>
         options.WithOrigins("http://localhost:8080")
                .AllowAnyHeader()
                .AllowAnyMethod());
    
  • Sørg for at du legger dette til etter - app.UseRouting();

    Se på bildet nedenfor (fra MSDN) for å se rekkefølgen på mellomvaren:

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

Svar #7

Å bruke dataType: 'jsonp' fungerte for meg.

   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               

Relaterte artikler