Postmanでこのエンドポイント、http://catfacts-api.appspot.com/api/facts?number=99
にアクセスすると、JSON
が返ってきます。
また、create-react-appを使用しているので、サーバーの設定を避けたいと思っています。
私のクライアントコードでは、同じことをするために fetch
を使おうとしていますが、エラーが発生します。
リクエストされたリソースに 'Access-Control-Allow-Origin'ヘッダが存在しません。 リソースです。したがって、Origin 'http://localhost:3000' は許可されていません。
アクセスが許可されていません。不透明な応答が必要な場合は、リクエストの's モードを 'no-cors'に設定し、CORSを無効にしてリソースを取得してください。
そこで私は、CORSを無効にするFetchに、次のようにオブジェクトを渡そうとしています。
fetch('http://catfacts-api.appspot.com/api/facts?number=99', { mode: 'no-cors'})
.then(blob => blob.json())
.then(data => {
console.table(data);
return data;
})
.catch(e => {
console.log(e);
return e;
});
興味深いことに、私が受け取ったエラーは、実際にはこの関数の構文エラーです。というのも、{ mode: 'no-cors' } オブジェクトを削除して、別の URL を指定すると、正常に動作するからです。
また、オブジェクト { mode: 'opaque'}
を渡すことも試みましたが、これは上記の元のエラーを返します。
私がすべきことは、CORSを無効にすることだと思います。何が足りないのでしょうか?
mode: 'no-cors'を追加しても、魔法のように物事がうまくいくわけではありません。なぜなら、この効果はブラウザに *「私のフロントエンドの JavaScript コードが、どんな状況でもレスポンスのボディやヘッダの内容を見ないようにブロックしろ」* と伝えることになるからです。 フロントエンドのJavaScriptからのクロスオリジンリクエストで何が起こるかというと、ブラウザはデフォルトでフロントエンドのコードがクロスオリジンのリソースにアクセスするのをブロックします。もしサイトがレスポンスで
Access-Control-Allow-Originを送信していれば、ブラウザはそのブロックを緩和し、あなたのコードがレスポンスにアクセスできるようにします。 しかし、あるサイトがレスポンスに
Access-Control-Allow-Originヘッダを送信していない場合、フロントエンドのコードがそのサイトからのレスポンスに直接アクセスすることはできません。特に、
mode: 'no-cors'`を指定しても解決できません(実際には、フロントエンドコードがレスポンスの内容にアクセスできないことが確実になってしまいます)。
*ただし、次のようにCORSプロキシを経由してリクエストを送信すると、うまくいきます。
var proxyUrl = 'https://cors-anywhere.herokuapp.com/',
targetUrl = 'http://catfacts-api.appspot.com/api/facts?number=99'
fetch(proxyUrl + targetUrl)
.then(blob => blob.json())
.then(data => {
console.table(data);
document.querySelector("pre").innerHTML = JSON.stringify(data, null, 2);
return data;
})
.catch(e => {
console.log(e);
return e;
});
<pre></pre>
注意:もしhttps
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
を付けるのではなく、自分のインスタンスのURLを前に付けます。例: https://cryptic-headland-94862.herokuapp.com/https://example.com.http://catfacts-api.appspot.com/api/facts?number=99
にアクセスできます。
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS は、Postman でレスポンスにアクセスできるにもかかわらず、レスポンスに Access-Control-Allow-Origin
レスポンスヘッダーが含まれていない限り、ブラウザはウェブアプリで実行されているフロントエンドの JavaScript コードからレスポンスにクロスオリジンでアクセスすることを許可しないという理由を説明しています。
http://catfacts-api.appspot.com/api/facts?number=99 には Access-Control-Allow-Origin
レスポンスヘッダがないので、フロントエンドのコードがレスポンスにクロスオリジンでアクセスすることはできません。
ブラウザがレスポンスを取得するのは問題なく、Postmanやブラウザのdevtoolsでも確認できますが、だからといってブラウザがそれをコードに公開するとは限りません。レスポンスには Access-Control-Allow-Origin
レスポンスヘッダがないので、そうはなりません。ですから、代わりにプロキシを使って取得する必要があります。
プロキシはそのサイトにリクエストを行い、レスポンスを取得し、Access-Control-Allow-Origin
レスポンスヘッダと必要なその他のCORSヘッダを追加して、それをあなたのリクエストコードに返します。そして、Access-Control-Allow-Origin
ヘッダーが追加されたレスポンスがブラウザに表示されるので、ブラウザはフロントエンドのコードに実際にレスポンスにアクセスさせることができます。mode: 'no-corner'
を使いたいとは思わないでしょうし、たとえそうであっても、自分が何をしているのか、その効果が何なのかを正確に知っている場合に限られます。というのも、mode: 'no-corner'
を設定することでブラウザに伝わるのは、 「私のフロントエンドの JavaScript コードが、どんな状況でもレスポンスのボディやヘッダの内容を調べないようにしてください」 ということだからです。 ほとんどの場合、これは明らかにあなたが望んでいることではありません。mode: 'no-corner'`の使用を検討したい場合については、詳細はhttps://stackoverflow.com/questions/39109789/what-limitations-apply-to-opaque-responses/39109790#39109790*の回答を参照してください。その要点は、以下のようなケースです。
<script>
, <link rel=stylesheet>
, <img>
, <video>
, <audio>
, <object>
, <embed>
, <iframe>.
要素のコンテンツにするという限られたケースがあります (これらはオリジンをまたいだリソースの埋め込みが許可されているので動作します)。しかし、何らかの理由で、ドキュメントのマークアップがリソースのURLを要素の href
または src
属性として使用するだけでは、これを行いたくない、または行えない場合があります。また、{ mode: 'opaque'}
というオブジェクトを渡そうとしました。
mode: 'opaque'というリクエストモードは存在しません。
opaqueは単なる *response* のプロパティであり、ブラウザは
no-cors` モードで送信されたリクエストからのレスポンスにこの不透明なプロパティを設定します。
ところで、opaqueという言葉は、最終的に得られるレスポンスの性質について、かなり明確なシグナルです。「不透明」というのは、見えないということです。
私にとっての解決策は、サーバーサイドで行うことでした。
C#のWebClient
ライブラリを使って、データ(私の場合は画像データ)を取得し、クライアントに送り返しました。あなたが選んだサーバーサイドの言語にも、似たようなものがあるかもしれません。
//Server side, api controller
[Route("api/ItemImage/GetItemImageFromURL")]
public IActionResult GetItemImageFromURL([FromQuery] string url)
{
ItemImage image = new ItemImage();
using(WebClient client = new WebClient()){
image.Bytes = client.DownloadData(url);
return Ok(image);
}
}
自分のユースケースに合わせて微調整することができます。重要なのは、client.DownloadData()
がCORSエラーなく動作したことです。通常、CORSの問題はWebサイト間でのみ発生するため、サーバーから 'cross-site'リクエストを行っても問題ありません。
そして、Reactのフェッチコールは以下のように簡単です。
//React component
fetch(`api/ItemImage/GetItemImageFromURL?url=${imageURL}`, {
method: 'GET',
})
.then(resp => resp.json() as Promise<ItemImage>)
.then(imgResponse => {
// Do more stuff....
)}
非常に簡単な解決策(設定に2分)は、npm
のlocal-ssl-proxyパッケージを使用することです。
**使い方は簡単で、とてもシンプルです。
1.パッケージをインストールします。
npm install -g local-ssl-proxy<br> 2.2.
local-ssl-proxy -source 9001 --target 9000<br>で
local-server`を実行しながらマスクします。
P.S: --target 9000
は -- "number of your port"
に、--source 9001
は --source "number of your port +1"
に置き換えてください。