要求されたリソースに 'Access-Control-Allow-Origin' ヘッダーが存在しない (REST API からデータを取得しようとしている場合)

javascript api cors fetch-api preflight


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属性によって制御されます。




Answer 1 sideshowbarker


この回答は多くの範囲をカバーしているため、3つの部分に分かれています。

  • CORSプロキシを使用して「Access-Control-Allow-Originヘッダーがない」問題を回避する方法
  • CORSのプリフライトを回避する方法
  • 「Access-Control-Allow-Originヘッダーをワイルドカードにすることはできません」の問題を修正する方法

CORSプロキシを使用して「Access-Control-Allow-Originヘッダーがない」問題を回避する方法

フロントエンドのJavaScriptコードがリクエストを送信しているサーバーを制御しておらず、そのサーバーからの応答の問題が、必要な Access-Control-Allow-Origin ヘッダーの欠如である場合でも、問題なく機能します。 -CORSプロキシを介して要求を行う。これがどのように機能するかを示すために、最初に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?"))

catch ブロックがヒットする理由は、ブラウザがそのコードが https://example.com から返される応答にアクセスできないようにするためです。そして、ブラウザーがそれを行う理由は、応答に Access-Control-Allow-Origin 応答ヘッダーがないことです。

これがまったく同じ例ですが、CORSプロキシが追加されています。

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?"))

注:https://cors-anywhere.herokuapp.comがダウンしているか、使用できない場合、Herokuに独自のCORS Anywhereサーバーをわずか2〜3分でデプロイする方法については、以下を参照してください。

リクエストURLを取り、それを変えるために成功応答にアクセスすることができます上記の2番目のコードスニペットhttps://cors-anywhere.herokuapp.com/https://example.comだけプロキシ原因URLでそれを前に置く-byそのプロキシを介して行われるように要求すると、次のようになります。

  1. リクエストを https://example.com に転送します。
  2. https://example.com から応答を受信します。
  3. Access-Control-Allow-Origin ヘッダーを応答に追加します。
  4. そのレスポンスにヘッダを追加したものを、リクエストしているフロントエンドのコードに渡します。

次に、ブラウザーはフロントエンドコードが応答にアクセスすることを許可します。これは、 Access-Control-Allow-Origin 応答ヘッダーを持つ応答がブラウザーに表示されるためです。

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

これらのコマンドを実行すると、https://cryptic-headland-94862.herokuapp.com/などの独自のCORS Anywhereサーバーが実行されます。したがって、リクエストURLの前に https://cors-anywhere.herokuapp.com を付けるのではなく、独自のインスタンスのURLを前に付けます。例:https://cryptic-headland-94862.herokuapp.com/https://example.com

したがって、https://cors-anywhere.herokuapp.comを使用しようとしたときにダウンしていることが判明した場合(ダウンする場合もあります)、Herokuアカウントを取得することを検討してください(まだダウンしていない場合)。上記の手順を実行してHerokuに独自のCORS Anywhereサーバーをデプロイするには、3分かかります。

独自に実行する場合でも、https://cors-anywhere.herokuapp.comまたはその他のオープンプロキシを使用する場合でも、リクエストがブラウザーをトリガーしてCORSプリフライト OPTIONS リクエストを実行するリクエストであっても、このソリューションは機能します。プロキシは、プリフライトを成功させるために必要な Access-Control-Allow-Headers および Access-Control-Allow-Methods ヘッダーも送り返します。


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

質問のコードはCORSプリフライトをトリガーします。これは、 Authorization ヘッダーを送信するためです。

https://developer.mozilla.org/en-US/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)である必要があります。それが他のステータスコードの場合、ブラウザはそこですぐに停止します。

問題のサーバーは501ステータスコードで OPTIONS リクエストに応答しています。これは明らかに、 OPTIONS リクエストのサポートを実装していないことを示していることを意味しています。この場合、他のサーバーは通常、405「メソッドは許可されていません」ステータスコードで応答します。

したがって、サーバーが OPTIONS リクエストに405または501または200または204以外のもので応答する場合、またはサーバーが応答しない場合、フロントエンドJavaScriptコードからそのサーバーに直接 POST リクエストを送信することはできません。これらの必要な応答ヘッダー。

ご質問のケースでプリフライトを発動させない方法は

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

「Access-Control-Allow-Originヘッダーをワイルドカードにすることはできません」の問題を修正する方法

別のエラーメッセージが出ています。

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

認証情報を含むリクエストの場合、 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-* ヘッダがあるレスポンスヘッダ。リクエストでそれらを送信することは決してありません。唯一の効果は、ブラウザにプリフライトをトリガーすることです。




Answer 2 Rakesh


このエラーは、クライアントURLとサーバーURLがポート番号を含めて一致しない場合に発生します。この場合、CORS(クロスオリジンリソース共有)に対応したサービスを有効にする必要があります。

Spring RESTサービスをホストしている場合は、Spring FrameworkのCORSサポートのブログ投稿で見つけることができます。

Node.js サーバーを使用してサービスをホストしている場合は

  1. Node.jsサーバーを停止します。
  2. npm install cors --save
  3. 次の行をserver.jsに追加します。

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration



Answer 3 Lex Soft


この問題は、フロントエンドのリクエストヘッダーとして次のコードを追加したために発生しました。

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

これらのヘッダーは、リクエストではなくレスポンスに属します。したがって、次の行を含めて削除します。

headers.append('GET', 'POST', 'OPTIONS');

リクエストに「Content-Type:application / json」が含まれていたため、いわゆるCORSプリフライトがトリガーされました。これにより、ブラウザはOPTIONSメソッドを使用してリクエストを送信しました。詳細については、CORSプリフライトを参照してください。
したがって、バックエンドでは、次を含む応答ヘッダーを返すことにより、このプリフライトされた要求を処理する必要があります。

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

もちろん、実際の構文はバックエンドに使用するプログラミング言語に依存します。

フロントエンドでは、次のようになります。

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));
}



Answer 4 Harrison O


私の場合、以下のような解決策を使用します。

フロントエンドまたは角度

post(
    this.serverUrl, dataObjToPost,
    {
      headers: new HttpHeaders({
           'Content-Type':  'application/json',
         })
    }
)

バックエンド(私は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);



Answer 5 Yair Levy


CORSプロキシを使用して「 Access-Control-Allow-Origin ヘッダーがありません」の問題を回避する方法に関する私の2セント...

バックエンドでphpを使用している場合、「CORSプロキシ」のデプロイは次のように簡単です。

  1. no-cors.php」という名前のファイルを作成して、以下の内容のファイルを作成します。

    $URL = $_GET['url']; echo json_encode(file_get_contents($URL)); die();

  2. のようなことをしてください。

    fetch('https://example.com/no-cors.php' + '?url=' + url) .then(response=>{*/Handle Response/*})