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:
- Viderekobler forespørselen til
https://example.com. - Mottar svaret fra
https://example.com. - Legger til
Access-Control-Allow-Origin-headeren i responsen. - 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 avPOST-forespørselen eller som en spørringsparameter - hvis serveren ikke krevde at
POST-kroppen hadde medietypenContent-Type: application/json, men i stedet akseptertePOST-kroppen somapplication/x-www-form-urlencodedmed en parameter kaltjson(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å
- Stopp Node.js-serveren.
npm install cors --save- 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
