1. ホーム
  2. javascript

[解決済み] REST APIからデータを取得しようとしたときに、要求されたリソースに'Access-Control-Allow-Origin'ヘッダーが存在しない。

2022-03-17 07:39:42

質問

HP AlmのREST APIからデータを取り出そうとしています。それは小さなcurlスクリプトでかなりうまく動作します - 私は私のデータを取得します。

今、JavaScript、フェッチと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 Pluginを使用してみましたが、別のエラーメッセージが表示されます。

レスポンスに含まれる 'Access-Control-Allow-Origin' ヘッダーの値は、以下のとおりです。 リクエストの認証モードが「*」の場合、ワイルドカード「*」であってはなりません。 'include'です。オリジン ' http://127.0.0.1:3000 は許可されません。 にアクセスできるようになります。によって開始されるリクエストの認証情報モード。 XMLHttpRequest は withCredentials 属性によって制御される。

解決方法は?

この回答は多くの分野をカバーしているので、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/ .
また、自作のプロキシをHerokuにデプロイするのも、5つのコマンドで2~3分程度で簡単にできます。

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-HeadersAccess-Control-Allow-Methods ヘッダはプリフライトを成功させるために必要なものです。


CORSプリフライトを回避する方法

質問のコードは、CORSプリフライトをトリガーします。 Authorization ヘッダを生成します。

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

それを抜きにしても 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_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 "Method not allowed" というステータスコードで応答します。

そのため、決して POST に応答した場合、フロントエンドのJavaScriptコードからそのサーバーに直接リクエストすることができます。 OPTIONS リクエストに 405 や 501、あるいは 200 や 204 以外の応答があった場合、あるいは必要な応答ヘッダがない場合は、そのような応答があります。

質問のケースでプリフライトを発動させない方法は、次のようになります。

  • もしサーバーが Authorization リクエストヘッダの本文に埋め込まれた認証データに依存しています。 POST リクエスト、またはクエリパラメータとして
  • を必要としない場合、サーバは POST ボディに Content-Type: application/json というメディアタイプを受け入れましたが、代わりに POST のボディを application/x-www-form-urlencoded という名前のパラメータを持つ json (またはその他)のJSONデータです。

修正方法 "Access-Control-Allow-Originヘッダーは、ワイルドカードであってはならない" 問題点

<サブ 別のエラーメッセージが表示されるのですが。

<サブ レスポンスに含まれる 'Access-Control-Allow-Origin' ヘッダーの値 <サブ は、リクエストの認証モードが <サブ 'include'を使用します。オリジン ' http://127.0.0.1:3000 は許可されません。 <サブ にアクセスできるようになります。によって開始されるリクエストのクレデンシャルモードは XMLHttpRequest は withCredentials 属性によって制御される。

認証情報を持つリクエストの場合、ブラウザはフロントエンドの JavaScript コードに、もし Access-Control-Allow-Origin ヘッダーが * . その代わりに、この場合の値はあなたのフロントエンド・コードのオリジンと正確に一致しなければなりません。 http://127.0.0.1:3000 .

参照 認証されたリクエストとワイルドカード を、MDN HTTP アクセス制御 (CORS) の記事で紹介しています。

リクエストを送信するサーバを制御している場合、このケースに対処する一般的な方法は、サーバ側で Origin リクエストヘッダを作成し、それを echo/reflect して Access-Control-Allow-Origin レスポンスヘッダを生成することができます(例:nginx)。

add_header Access-Control-Allow-Origin $http_origin

しかし、これは単なる例です。他の(ウェブ)サーバーシステムでも、同様の方法でオリジン値をエコーすることができます。


<ブロッククオート

Chromeを使用しています。また、そのChrome CORS Pluginを使用してみました。

そのChrome CORSプラグインは、どうやら単純に考えても Access-Control-Allow-Origin: * ヘッダをブラウザが見るレスポンスに追加します。もしこのプラグインがもっと賢かったら、この偽の Access-Control-Allow-Origin レスポンスヘッダを、フロントエンドの JavaScript コードの実際のオリジンに設定します。 http://127.0.0.1:3000 .

だから、テスト用でもそのプラグインを使うのは避けてください。邪魔になるだけです。ブラウザがフィルタリングしていない状態で、サーバからどのようなレスポンスが返ってくるかをテストするには curl -H と同じです。


フロントエンドのJavaScriptのコードとしては fetch(…) のリクエストは、質問の中にあります。

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

これらの行を削除してください。その Access-Control-Allow-* ヘッダーは レスポンス ヘッダを生成します。リクエストでは決して送らないようにしましょう。ブラウザがプリフライトを行うきっかけになるだけの効果しかありません。