REST API からデータを取得しようとしたときに、要求されたリソースに「Access-Control-Allow-Origin」ヘッダーが存在しません。質問する

REST API からデータを取得しようとしたときに、要求されたリソースに「Access-Control-Allow-Origin」ヘッダーが存在しません。質問する

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 をプレフィックスとして追加すると、リクエストはプロキシ経由で行われるようになり、次のようになります。

  1. リクエストを に転送しますhttps://example.com
  2. からの応答を受信しますhttps://example.com
  3. Access-Control-Allow-Origin応答にヘッダーを追加します。
  4. 追加されたヘッダーを含む応答を、要求元のフロントエンド コードに返します。

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を持つクロスオリジンの受信をオプトインしているかどうかを判断しますAuthorizationContent-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_inURL に置き換えられます。

ブラウザがそのリクエストから必要とする応答には、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-urlencodedjson

「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-*ヘッダーです。リクエストで送信することは絶対に避けてください。その唯一の効果は、ブラウザーがプリフライトを実行するようにトリガーすることです。

おすすめ記事