極力バックエンドを書かずに異なるドメインのWebサイトからデータを取得するCORS Proxyというのを作った
作ったと言っても、実際コードを書いたのは2年近く前だったのだけど。
当時書いてたアプリのために作っておいたのをずっと放置していたが、最近ちょっと使う用途があったので久しぶりに触ったら何もかも忘れてて(README書いてなかった)えらい苦労した。改めて使い方調べながらREADMEまとめたので、ついでに気づいたバグとか直してGitHubで公開した。
CORSとは
Cross-Origin Resource Sharing (CORS) は、追加の HTTP ヘッダーを使って、あるオリジン (ドメイン) で動いているウェブアプリケーションが、異なるオリジンのサーバーのリソースにアクセスできるようにする仕組み
雑に説明すると、サーバ側でAccess-Control-Allow-Origin
ヘッダーをレスポンスで返してなんやかんやするやつ。
今回作ったやつは追加のリクエストヘッダーをいくつか必要としているので、Access-Control-Allow-Headers
ヘッダーのレスポンスやプリフライトリクエスト(OPTIONSメソッドのリクエストが本命の前に実行される)への対応も必要になっている。
使い方
環境準備
Herokuで動かすようにしてあるので、リポジトリをクローンしたらHerokuアプリケーション作って、認証用の環境変数を設定してデプロイする。
$ git clone https://github.com/ushiboy/cors-proxy $ cd cors-proxy $ heroku create
認証用のキーはbin/generate_key
でランダムに生成する。
$ bin/generate_key [Key]: tV4VWOcq0HbJ... [Digest]: $2y$10$do7go...
認証用の環境変数はAUTH_KEY_DIGEST
に生成したDigest
の値を、ALLOW_ORIGIN
に許可するオリジン(複数指定する場合はカンマで区切る)を設定する。
$ heroku config:set AUTH_KEY_DIGEST='$2y$10$do7go...' ALLOW_ORIGIN=http://myapp.com
プッシュしてデプロイする。
$ git push heroku master
Webフロントエンド側からの利用
デプロイ先のHerokuアプリケーションのURLに、生成した認証用のKey
の方をAuthorization
リクエストヘッダーにBearer
としてつけて、クエリストリングのq
パラメータに取得対象のURLを指定してリクエストを発行する。
const q = encodeURIComponent('http://www.example.com/data.txt'); const result = await fetch(`https://<your_app_name>.herokuapp.com/?q=${q}`, { headers: { 'Authorization': `Bearer tV4VWOcq0HbJ...` } });
これでプリフライトリクエストのあとに正しく取得ができれば動作はOK。
取得対象が古のCP932とかCP51932とかUTF-8ではない場合はX-From-Charset
ヘッダーをつけてリクエストすることで、UTF-8にエンコードして取得できる。
const q = encodeURIComponent('http://www.example.com/data.txt'); const result = await fetch(`https://<your_app_name>.herokuapp.com/?q=${q}`, { headers: { 'Authorization': `Bearer tV4VWOcq0HbJ...`, 'X-From-Charset': 'CP51932' } });
X-From-Charset
に指定できる文字コードの種類はPHPのmbstringがサポートしてるやつ。
補足
認証キーとオリジンでアクセス制限するようにしているけど、これは気休め程度。Webフロントエンド以外のところでやろうと思えばどうにでもできるので、フロントエンドのコードにベタ書きして使うかは要検討。
自分はHerokuアプリケーションのURLと認証キーをローカルストレージに設定するようにしてコードにベタ書きはせずに使ってる。
GETしかできないものなので、そこまで気にしなくても良いかも...という気もしないでもない。
とりあえずちょっと外部Webサイトのリソースも使いたいWebアプリ書くとかだったら、フロントエンド側はGithub Pagesでホストしてそこから利用するとかできると思う。