LoginSignup
22
16

More than 1 year has passed since last update.

【CORSエラー】初心者がJavaScriptから外部APIを叩いたときの話

Posted at

初心者のつまづきポイントでした。
もし、あやまっている点や補足、実はこんな解決法がある、などありましたらどしどしご指摘ください!

まずは「フロントから外部APIを叩いたときのCORSエラー」の結論

3.の場合、フロントエンドからはほぼ無理!バックエンドから処理しましょう!

フロント言語のJavascriptを使用して、ブラウザからAPIを利用するとき、大まかに以下の3つにわかれます。

  1. APIキーを事前に取得し、アクセスできる(Google Map APIなど)
  2. APIキー無しにアクセスできる(NHKの番組表取得など)
  3. APIキー無しにアクセスできるが、フロントからではCORSエラーで アクセスが難しい(なんで!?)

初学者の私は丸1日格闘してしまいまして、 「なぜ、CORSエラーが起こるか」「そもそもAPIって?」 →APIを使用するための 「HTTPリクエストやHTTPレスポンス」 について調べたことをつらつら書いていきます。

さー行ってみよー!


目次

そもそもAPIって
HTTPリクエストやHTTPレスポンス
なぜ、CORSエラーが起こるか


そもそもAPIって

APIの解説や使い方について、正確な話や詳細な使い方は他の記事に譲るとして、 「とりあえず、サクッと教えてくれよ!」 ということで少し整理したいと思います。
この説明から入るのもどうかと思いますが、APIとは「Application Programming Interface」の略です。つまり、アプリケーション内の プログラムとプログラムを繋ぐものといったとこでしょうか。
APIの使われ方は、以下のように大きく2種類ありまして、私はまずここで引っかかってしまいました。
※ここは今回のCORSエラーの根幹でもあったりするので、少し頭の片隅にでも

  • 今回のようにWeb上で公開されている他者のAPI
    └皆さんが使いたいのはこちらかなと。
  • 自分で書いたプログラムを別のプログラムから呼び出すためのAPI
    └そんなことあるの?って感じですが、サーバAに立てたプログラムからサーバBに立てたプログラムを 通信して利用するというイメージです

ま、 「はよ、APIの使い方教えろや!」 ってところでしょうので、ざっくりとAPIが 通信を介してプログラムとプログラムをつなげるものという理解をしてもらえたところで、JavaScriptでの簡単な記述のひとつを紹介します。
今回はfetchというものを紹介します。

const url = "https://hogehoge.com/hugahuga.json" // 指定するURLは各APIのドキュメントを読みましょう
fetch('url')
  .then(response => response.json())
  .then(data => console.log(data));

fetchを使うとこんなに簡単にAPIが使えたりするんですね!
少し解説・補足を。
【解説】
1行目、APIのURLを指定します。
2行目、fetchすることで、後述のリクエストとレスポンスを処理します。
3行目、Promiseの記述式である.thenfetchしてきたPromiseが完了したらそのデータをresponseという引数として取り込み、response.json()を処理します。
4行目、3行目と同様に3行目の処理が完了したらそのデータをdataという引数として取り込み、console.log(data)という処理を行います。
【補足】
fetchPromiseを返します。Promiseの解説は私のこちらの記事にまとめています。参考とした記事も多く載せているので、マスターしたい方はそちらで!
②アロー関数で記述していますが、普通の書き方でも問題ないです。

Promiseについてざっくり。

  • Javascriptは非同期処理: 原則上から下に処理が進みますが、DBからのgetなどのような時間がかかる処理は並行して進みます。
  • Promiseは処理の状態をもったオブジェクト: 処理完了(resolved)などのような「状態」を持たせることができ、処理の順序をコントロールすることができるようになります。
  • .thenPromiseresolvedという状態であるときに行う処理: 条件分岐のようなもので、Promiseの処理が失敗したときに行われる.catchというものもあったりします。
  • 非同期処理関数を定義するasyncと演算子await asyncで定義した関数内でawaitを使用すると演算子の後に来るものがresolvedになったら進みます。

さて、サンプルとして郵便番号から住所を取得するzipcloudのAPIを試してみましょう。
指定するURLのパラメータ(URLの?以降の zipcode=1000001)などはAPIのドキュメントに使い方が書いてあるので、使いたいAPIのものを見てみましょう。(ちなみに郵便番号100-0001は皇居のある東京都千代田区千代田です)

async function apiRequest() {
  const downloadUrl =
    "http://zipcloud.ibsnet.co.jp/api/search?zipcode=1000001";
  const response = await fetch(downloadUrl);
  const json = await response.json();
  const string = JSON.stringify(json);
  console.log(string);
}

apiRequest();

HTTPリクエストやHTTPレスポンス

これまた、解説や使い方について、正確な話や詳細な使い方は他の記事に譲るとして、 「とりあえず、サクッと教えてくれよ!」 ということで少し整理したいと思います。(1億万回こすられている話だと思いますが...)

正確性を無視して、インターネットを使うということは、おおまかに以下のような手順を踏んでいます。
人間「○○のサイトの情報をみたい!」
ブラウザ「では、○○のサイトのサーバにアクセスして情報を取ってきます」
ブラウザ「○○のサイトさん、情報ください!」
サーバ「良いですよ、どーぞ」
ブラウザ「人間さん、取ってきました!どーぞ」
人間「ふむふむなるほど」

上記で、 「指定のサーバにアクセスして情報をとってこい」 という指示を書いているものが、(おそらく)あなたのJavaScriptのプログラムで、「情報ください」 というものが リクエスト「良いですよ、どーぞ」 というものが レスポンス になります。

前項のfetchはリクエストの指示を出してレスポンスを受け取るところまでやります。

リクエストとレスポンスの詳細については、大きく割愛します。
ざっくりと、HTTP(超すげーテキストを送受信する規則Hyper Text Transfer Protocol)リクエストでは、やりとりの種類を指定するHTTPリクエストメソッドがあります。(GET参照、POST新規作成、PUT更新、DELETE削除など)
リクエストには、このようなメソッドや、キャッシュの扱い、POST時などの送信内容などなど様々な指示/情報が含まれています。
レスポンスには、リクエストされた結果や返されるデータなどが含まれます。
fetchでのリクエストの指示内容は以下のように第二引数に記述することができます。それぞれの詳細はドキュメントをご参照ください。なお、コメントの*印はデフォルトの値です。
(前項ではこの第二引数を指定していなかったので、GETメソッドのように、デフォルトで処理されていた ということですね!)

fetch(url, {
  method: "GET", // *GET, POST, PUT, DELETE, etc. 
  mode: "cors", // no-cors, *cors, same-origin
  cache: "default", // *default, no-cache, reload, force-cache, only-if-cached
  credentials: "same-origin", // include, *same-origin, omit
  headers: {
    "Content-Type": "application/json",
  },

});

fetchのドキュメントは以下

なぜ、CORSエラーが起こるか

ようやく、本題です。
まず、CORSとはなんだ。
CORS(オリジン間のリソース共有Cross-Origin Resource Sharing)、オリジンというものは、ドメインと変換して良いかと思います。(正確にはドメイン+プロトコル+ポート番号)
例えば、以下のような異なるドメインからアクセスしたときが対象となります。

  1. あなた のローカルサーバから外部APIのサーバにアクセスするとき
  2. あなたのサーバAからあなたのサーバBにアクセスするとき

ネットにあるCORSエラーの対処法(=解決できるもの)はほとんどがレスポンス側を管理できる2つ目の場合です。
つまり、今回の場合(フロントからJavaScriptで外部APIをたたく場合)はほぼ無理という結論に落ち着きました。
使いたいときはバックエンドから処理しましょう
理由は後述します。

異なるオリジンからの不正アクセスを禁止するという目的で、このCORSの制限があるということです。
そうなると、この世のほとんどの通信は異なるオリジンからのアクセスということになってしまうので、「○○というオリジンからのアクセスは許可するよ」 という指示が必要になります。
前項で説明したリクエストにはオリジンというものが含まれております。レスポンスには「リクエストされたオリジンからのアクセスを許可するよ」という記述を含むことができます。
※ブラウザの検証モードでNetworkというタブを開くとリクエスト&レスポンスの中身を見ることができます。

結論の理由

つまり、「CORSでのアクセスを許可するかどうか」「レスポンス側に委ねられている」 のであって、「リクエスト側である私たちではどうしようもない」 ということなのです。(当たり前っちゃ当たり前ですが...)

ネット上にあるCORSエラーの対処法のほとんどは上記の2.「あなたのサーバAからあなたのサーバBにアクセスするとき」のものであり、レスポンス側の設定を変えましょうというものがほとんどでした。
なお、CORS回避するためのChrome拡張機能などありますが、本番環境にアップロードすると考えている場合は、普通にバックエンドから処理しましょう。

とほほ...

22
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
16