Snažím sa získať nejaké údaje z rozhrania REST API spoločnosti HP Alm. S malým curl skriptom to funguje celkom dobre - dostanem svoje údaje.
Teraz sa zdá, že robiť to s JavaScriptom, fetch a ES6 (viac-menej) je väčší problém. Stále dostávam túto chybovú správu:
Fetch API nemôže načítať
. Odpoveď na požiadavku preflight doesn't neprešla kontrolou kontroly prístupu: No 'Access-Control-Allow-Origin' header is prítomná na požadovanom zdroji. Pôvod 'http://127.0.0.1:3000' je preto nie je povolený prístup. Odpoveď mala stavový kód HTTP 501. Ak neprehľadná odpoveď slúži vašim potrebám, nastavte režim požiadavky'na 'no-cors' na získanie zdroja s vypnutým CORS.
Chápem, že je to preto, lebo sa snažím načítať tieto údaje z môjho localhostu a riešenie by malo používať CORS. Teraz som si myslel, že som to skutočne urobil, ale nejako to buď ignoruje to, čo napíšem do hlavičky, alebo je problém v niečom inom?
Je teda problém v implementácii? Robím to zle? Do logov servera sa bohužiaľ nemôžem pozrieť. Som tu naozaj trochu zaseknutý.
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));
}
Používam prehliadač Chrome. Skúšal som použiť aj ten Chrome CORS Plugin, ale potom dostávam ďalšiu chybovú správu:
Hodnota hlavičky 'Access-Control-Allow-Origin' v odpovedi
nesmie byť zástupný znak '*', keď je režim poverenia požiadavky'na 'include'. Pôvod 'http://127.0.0.1:3000' preto nie je povolený prístup. Režim poverení pri požiadavkách iniciovaných XMLHttpRequest je riadený atribútom withCredentials.
Táto odpoveď zahŕňa veľa tém, preto je rozdelená do troch častí:
Ako použiť proxy server CORS na obídenie problémov "Žiadna hlavička Access-Control-Allow-Origin "
Ak nemáte kontrolu nad serverom, na ktorý váš frontendový kód JavaScriptu posiela požiadavku, a problém s odpoveďou z tohto servera spočíva len v absencii potrebnej hlavičky Access-Control-Allow-Origin
, stále môžete dosiahnuť, aby všetko fungovalo - požiadavku môžete odoslať prostredníctvom proxy servera CORS. Aby sme ukázali, ako to funguje, najprv uvedieme kód, ktorý nepoužíva proxy server CORS:
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?"))
Dôvodom, prečo sa tam zasiahne blok catch
je, že prehliadač bráni tomuto kódu v prístupe k odpovedi, ktorá sa vracia z https://example.com
. A dôvodom, prečo to prehliadač robí, je, že v odpovedi chýba hlavička odpovede Access-Control-Allow-Origin
.
Teraz je tu presne ten istý príklad, ale len s pridaným 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?"))
Poznámka: Ak je https
https://example.com
.https://example.com
.Access-Control-Allow-Origin
.Access-Control-Allow-Origin
prehliadač vidí.
Pomocou kódu z https://github.com/Rob--W/cors-anywhere/.<br> môžete ľahko spustiť vlastný proxy server;
Vlastný proxy server môžete tiež ľahko nasadiť na Heroku doslova za 2-3 minúty pomocou 5 príkazov:git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master
https://cors-anywhere.herokuapp.com
, predponujte ju namiesto toho s adresou URL vašej vlastnej inštancie; napr. https://cryptic-headland-94862.herokuapp.com/https://example.com.
Ak sa teda pokúsite použiť httpsOPTIONS
- pretože v takom prípade proxy server odošle späť aj hlavičky Access-Control-Allow-Headers
a Access-Control-Allow-Methods
potrebné na úspešné vykonanie preflightu.Ako sa vyhnúť preflightu CORS
Kód v otázke spúšťa CORS preflight, pretože posiela hlavičku Authorization
.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
Aj bez toho by hlavička Content-Type: application/json
spustila preflight.
Čo znamená "preflight": predtým, ako prehliadač vyskúša POST
v kóde v otázke, najprv odošle požiadavku OPTIONS
na server - aby zistil, či server súhlasí s prijímaním cross-origin POST
, ktorý obsahuje hlavičky Authorization
a Content-Type: application/json
.
Funguje to celkom dobre s malým skriptom curl - dostanem svoje údaje. Ak chcete správne testovať pomocou
curl
, musíte emulovať požiadavkuOPTIONS
pred odoslaním, ktorú posiela prehliadač:
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"
...pričom https://the.sign_in.url
je nahradené akoukoľvek skutočnou adresou URL sign_in
.
Odpoveď, ktorú prehliadač potrebuje vidieť z tejto požiadavky OPTIONS
, musí obsahovať hlavičky, ako je táto:
Access-Control-Allow-Origin: http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization
Ak odpoveď OPTIONS
neobsahuje tieto hlavičky, prehliadač sa zastaví na tomto mieste a nikdy sa ani nepokúsi odoslať požiadavku POST
. Takisto stavový kód HTTP pre odpoveď musí byť 2xx - typicky 200 alebo 204. Ak je to akýkoľvek iný stavový kód, prehliadač sa zastaví na tomto mieste.
Server v otázke odpovedá na požiadavku OPTIONS
stavovým kódom 501, čo zrejme znamená, že sa snaží naznačiť, že neimplementuje podporu pre požiadavky OPTIONS
. Ostatné servery v tomto prípade zvyčajne odpovedajú stavovým kódom 405 "Metóda nie je povolená".
Nikdy teda nebudete môcť z vášho frontendového kódu JavaScriptu priamo zadávať požiadavky POST
na tento server, ak server na túto požiadavku OPTIONS
odpovie kódom 405 alebo 501 alebo iným kódom ako 200 alebo 204, alebo ak neodpovie týmito potrebnými hlavičkami odpovede.
Spôsob, ako sa vyhnúť spusteniu preflightu pre prípad uvedený v otázke, by bol nasledovný:
Authorization
, ale namiesto toho by sa (napríklad) spoliehal na autentifikačné údaje vložené do tela požiadavky POST
alebo ako parameter dotazuPOST
obsahovalo mediálny typ Content-Type: application/json
, ale namiesto toho prijal telo POST
ako application/x-www-form-urlencoded
s parametrom s názvom json
(alebo akýmkoľvek iným), ktorého hodnotou sú údaje JSONAko opraviť problémy s "Access-Control-Allow-Origin header must not be the wildcard "
Dostávam ďalšiu chybovú správu:
Hodnota 'Access-Control-Allow-Origin' hlavičky v odpovedi nesmie byť zástupný znak '', keď je režim poverenia požiadavky'> 'include'. Pôvod 'http://127.0.0.1:3000' preto nie je povolený prístup. Režim poverení pri požiadavkách iniciovaných XMLHttpRequest je riadený atribútom withCredentials. V prípade požiadavky, ktorá obsahuje poverenia, prehliadače nepovolia vášmu frontendovému kódu JavaScript prístup k odpovedi, ak je hodnota hlavičky odpovede
Access-Control-Allow-Origin
`. Namiesto toho musí hodnota v tomto prípade presne zodpovedať pôvodu vášho frontend kódu, teda
http://127.0.0.1:3000. Pozrite si časť [*Credentialed requests and wildcards*][1] v článku MDN Kontrola prístupu HTTP (CORS). Ak ovládate server, na ktorý posielate požiadavku, potom je bežným spôsobom, ako sa s týmto prípadom vysporiadať, nakonfigurovať server tak, aby prevzal hodnotu hlavičky požiadavky
Origina echo/odrazil ju späť do hodnoty hlavičky odpovede
Access-Control-Allow-Origin`. Napríklad s nginx: Jazyk: lang-none -->
add_header Access-Control-Allow-Origin $http_origin
Používam Chrome. Skúsil som použiť aj tento doplnok CORS pre Chrome Ten zásuvný modul CORS pre Chrome zrejme len prostoducho vstrekuje príkaz
Access-Control-Allow-Origin: *
do odpovede, ktorú prehliadač vidí. Keby bol tento zásuvný modul inteligentnejší, nastavil by hodnotu tejto falošnej hlavičky odpovedeAccess-Control-Allow-Origin
na skutočný pôvod vášho frontendového kódu JavaScriptu, tedahttp://127.0.0.1:3000
. Vyhnite sa preto používaniu tohto doplnku, dokonca aj na testovanie. Je to len rozptýlenie. Ak chcete otestovať, aké odpovede dostanete zo servera bez toho, aby ich prehliadač filtroval, je lepšie použiťcurl -H
, ako je uvedené vyššie.Pokiaľ ide o frontendový kód JavaScript pre požiadavku
fetch(...)
v otázke:
headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');
Odstráňte tieto riadky. Hlavičky Access-Control-Allow-*
sú hlavičky odpovede. Nikdy ich nechcete posielať v požiadavke. Jediný účinok, ktorý to bude mať, je spustenie prehliadača, aby vykonal preflight.
Táto chyba sa vyskytne, keď sa nezhoduje adresa URL klienta a adresa URL servera vrátane čísla portu. V takom prípade je potrebné povoliť službu pre CORS, čo je zdieľanie zdrojov medzi jednotlivými pôvodcami.
Ak hostujete službu Spring REST, potom ju nájdete v príspevku na blogu CORS support in Spring Framework.
Ak hostujete službu pomocou servera Node.js, potom
Zastavte server Node.js.
npm install cors --save
Do súboru server.js pridajte nasledujúce riadky
var cors = require('cors')
app.use(cors()) // Toto použite za deklaráciou premennej