HP Alm の REST API からデータを取得しようとしています。小さな curl スクリプトでうまく動作し、データを取得できました。
今では、JavaScript、fetch、ES6 (多かれ少なかれ) でそれを実行すると、より大きな問題になるようです。次のエラー メッセージが繰り返し表示されます。
Fetch API を読み込めません。プリフライト リクエストへの応答がアクセス制御チェックに合格しません: 要求されたリソースに 'Access-Control-Allow-Origin' ヘッダーが存在しません。したがって、オリジン 'http://127.0.0.1:3000' はアクセスを許可されません。応答の HTTP ステータス コードは 501 です。不透明な応答で十分な場合は、要求のモードを 'no-cors' に設定して、CORS を無効にしてリソースをフェッチしてください。
これは、ローカルホスト内からデータを取得しようとしているためであり、解決策としてはクロスオリジンリソース共有 (CORS)実際にそうしたつもりだったのですが、どういうわけかヘッダーに書いた内容が無視されるか、あるいは別の問題が起きているようです。
それで、実装に問題があるのでしょうか? やり方が間違っているのでしょうか? 残念ながら、サーバー ログを確認できません。本当に困っています。
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));
}
私は Chrome を使用しています。Chrome CORS プラグインも使用してみましたが、別のエラー メッセージが表示されます。
リクエストの資格情報モードが「include」の場合、レスポンスの「Access-Control-Allow-Origin」ヘッダーの値はワイルドカード「*」であってはなりません。したがって、オリジン「http://127.0.0.1:3000」はアクセスが許可されません。XMLHttpRequest によって開始されたリクエストの資格情報モードは、withCredentials 属性によって制御されます。
ベストアンサー1
この回答は広範囲にわたるため、3 つの部分に分かれています。
- CORS プロキシを使用して「Access-Control-Allow-Origin ヘッダーがありません」問題を回避する方法
- CORSプリフライトを回避する方法
- 「Access-Control-Allow-Origin ヘッダーにワイルドカードは使用できません」という問題を修正する方法
CORS プロキシを使用して「Access-Control-Allow-Origin ヘッダーがありません」問題を回避する方法
フロントエンド コードがリクエストを送信するサーバーを制御せず、そのサーバーからの応答の問題が必要なAccess-Control-Allow-Origin
ヘッダーの不足だけである場合、CORS プロキシを介してリクエストを送信することで、引き続き動作させることができます。
以下のコードを使って簡単に独自のプロキシを実行できます。https://github.com/Rob--W/cors-anywhere/
5 つのコマンドを実行するだけで、わずか 2 ~ 3 分で独自のプロキシを Heroku に簡単にデプロイできます。
git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master
これらのコマンドを実行すると、たとえば で独自の CORS Anywhere サーバーが実行されることになりますhttps://cryptic-headland-94862.herokuapp.com/
。
次に、リクエスト URL の前にプロキシの URL を追加します。
https://cryptic-headland-94862.herokuapp.com/https://example.com
プロキシ URL をプレフィックスとして追加すると、リクエストはプロキシ経由で行われるようになり、次のようになります。
- リクエストを に転送します
https://example.com
。 - からの応答を受信します
https://example.com
。 Access-Control-Allow-Origin
応答にヘッダーを追加します。- 追加されたヘッダーを含む応答を、要求元のフロントエンド コードに返します。
Access-Control-Allow-Origin
ブラウザは、レスポンス ヘッダーを含むレスポンスがブラウザに表示されるため、フロントエンド コードがレスポンスにアクセスできるようにします。
これは、リクエストがブラウザに CORS プリフライトOPTIONS
リクエストの実行をトリガーするものであっても機能します。その場合、プロキシはプリフライトを成功させるために必要なAccess-Control-Allow-Headers
およびヘッダーも送信するためです。Access-Control-Allow-Methods
CORSプリフライトを回避する方法
質問のコードはAuthorization
ヘッダーを送信するため、CORS プリフライトをトリガーします。
https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#プリフライトリクエスト
それがなくても、Content-Type: application/json
ヘッダーはプリフライトをトリガーします。
「プリフライト」の意味: ブラウザがPOST
質問のコードで を試す前に、まずOPTIONS
サーバーにリクエストを送信して、サーバーがおよびヘッダーPOST
を持つクロスオリジンの受信をオプトインしているかどうかを判断します。Authorization
Content-Type: application/json
小さな curl スクリプトでうまく動作し、データを取得できます。
を適切にテストするには、ブラウザが送信するcurl
プリフライトをエミュレートする必要があります。OPTIONS
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"
…https://the.sign_in.url
実際のsign_in
URL に置き換えられます。
ブラウザがそのリクエストから必要とする応答には、OPTIONS
次のようなヘッダーが必要です。
Access-Control-Allow-Origin: http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization
応答にこれらのヘッダーが含まれていない場合OPTIONS
、ブラウザはそこで停止し、リクエストの送信を試行しませんPOST
。また、応答の HTTP ステータス コードは 2xx (通常は 200 または 204) である必要があります。その他のステータス コードの場合、ブラウザはそこで停止します。
質問のサーバーは、OPTIONS
リクエストに対して 501 ステータス コードで応答します。これは、リクエストのサポートを実装していないことを示そうとしているようですOPTIONS
。この場合、他のサーバーは通常、405「メソッドは許可されていません」ステータス コードで応答します。
POST
したがって、サーバーがそのリクエストに対して 405 や 501 で応答したり、200 や 204 以外の値で応答したり、必要な応答ヘッダーで応答しなかったりする場合は、フロントエンドの JavaScript コードからそのサーバーに直接リクエストをOPTIONS
送信することはできません。
質問のケースでプリフライトのトリガーを回避する方法は次のとおりです。
- サーバーが
Authorization
リクエストヘッダーを必要とせず、代わりに、リクエスト本体に埋め込まれた認証データPOST
やクエリパラメータに依存している場合 - サーバーが
POST
本文にメディアタイプを要求せずContent-Type: application/json
、代わりにJSONデータである名前のパラメータ(または任意の名前)でPOST
本文を受け入れた場合application/x-www-form-urlencoded
json
「Access-Control-Allow-Origin ヘッダーにワイルドカードは使用できません」という問題を修正する方法
別のエラーメッセージが表示されます:
リクエストの資格情報モードが'include' の場合、レスポンスの 'Access-Control-Allow-Origin' ヘッダーの値はワイルドカード '*' であってはなりません。 したがって、オリジン ' ' はアクセスを許可されません。XMLHttpRequest によって開始されたリクエストの資格情報モードは、withCredentials 属性によって制御されます。
http://127.0.0.1:3000
Access-Control-Allow-Origin
認証情報を持つリクエストの場合、ヘッダーの値が であれば、ブラウザはフロントエンドの JavaScript コードがレスポンスにアクセスすることを許可しません*
。 代わりに、その場合の値はフロントエンド コードのオリジン と完全に一致する必要がありますhttp://127.0.0.1:3000
。
見る認証リクエストとワイルドカードMDN の HTTP アクセス制御 (CORS) の記事。
リクエストを送信するサーバーを制御している場合、このケースに対処する一般的な方法は、Origin
リクエスト ヘッダーの値を取得し、それを応答ヘッダーの値にエコー/反映するようにサーバーを構成することですAccess-Control-Allow-Origin
。たとえば、nginx の場合:
add_header Access-Control-Allow-Origin $http_origin
しかし、これは単なる例です。他の (Web) サーバー システムにも、元の値をエコーする同様の方法があります。
私はChromeを使用しています。Chrome CORSプラグインも試してみました
Chrome CORS プラグインは、Access-Control-Allow-Origin: *
どうやら単純に、ブラウザに表示されるレスポンスにヘッダーを挿入するだけのようです。プラグインがもっと賢いなら、偽のAccess-Control-Allow-Origin
レスポンス ヘッダーの値をフロントエンド JavaScript コードの実際のオリジンに設定するはずですhttp://127.0.0.1:3000
。
したがって、テストの場合でも、そのプラグインの使用は避けてください。邪魔になるだけです。ブラウザーでフィルタリングせずにサーバーからどのような応答が返されるかをテストするには、curl -H
上記のように使用する方がよいでしょう。
fetch(…)
質問のリクエストのフロントエンド JavaScript コードに関しては、次のとおりです。
headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');
上記の行を削除します。ヘッダーはレスポンスAccess-Control-Allow-*
ヘッダーです。リクエストで送信することは絶対に避けてください。その唯一の効果は、ブラウザーがプリフライトを実行するようにトリガーすることです。