前置き
nuxt generate
で作った静的サイトをGitHub Pagesにホスティングし,自分のはてなブックマークのRSSをAxiosでGETして表示させようとしていました.
何も考えずにブラウザで開くとこんなエラーが出てうまくいきません.
Access to XMLHttpRequest at 'http://api.example.com' from origin 'http://localhost.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
いわゆる,オリジン間リソース共有(CORS)における同一オリジンポリシー違反です.このエラーはブラウザにおいて,現在アクセスしてるサイトと異なるオリジンにあるリソースに対してリクエストを行うときに起きます.
今回の場合,CORS解決に必要なヘッダー情報がはてな側からのレスポンスに含まれていないことが原因で,ブラウザによって通信をブロックされてしまいました.CORS解決に必要なヘッダーというのはAccess-Control-Allow-Origin: *
のようなフィールドです.
参考: https://qiita.com/umechiki/items/82dd43cd1465de5f5afe
ちなみに,Nuxtでよく紹介されている@nuxtjs/proxyを使った回避方法はフロントエンド側だけで解決できますが,こいつはnuxt generate
では機能しません.まさに,こちらの方と同じ状況になりました.
以上のことから,代理でRSSを取得するプロキシを建てて,適切なヘッダを付与してレスポンスを返してくれるようにすれば,CORSを解決することができます.
この記事では,Zeit社のNowを使ってCORSエラーを簡単に解決できたのでメモします.
Now
NowはZeit社のPaaSで,シンプルを極めたようなデプロイ方法が特徴です.
Zeitのアカウントを作り,デプロイ対象のディレクトリで
$ npm i -g now
$ now login
$ now deploy
と実行していけば,ほぼ3ステップでデプロイできてしまいます.
Serverless Functionsを使うにはapi
というディレクトリを作り,その中にtarget.js
を置いておくとデプロイ時に自動的にビルドされ,https://<project-name>.<username>.now.sh/api/target
でアクセスできるようになります.
参考: https://zeit.co/docs/v2/serverless-functions/introduction
代わりにRSSを取って来てもらう
api
下に置くコードは,AxiosでGETしたはてなブックマークのレスポンスBodyにCORS解決に必要なヘッダを付与してリクエスト元に返すようにすればOKです.
constaxios=require('axios')module.exports=async(req,res)=>{awaitaxios.get('https://b.hatena.ne.jp/<username>/bookmark.rss?of=1').then((hatenaRes)=>{res.setHeader('content-type',hatenaRes.headers['content-type'])res.setHeader('Access-Control-Allow-Origin','https://<username>.github.io')res.status(200).send(hatenaRes.data)}).catch((e)=>{console.log(e)res.status(500).send('Internal Server Error.')})}
全て許す場合はres.setHeader('Access-Control-Allow-Origin', '*')
を設定してください.
これははてなのRSSを取るための小さな構成例ですが,req.headers
やres.setHeader()
を使うことでより細かな制御を行うことができます.Access-Control-Allow-Origin
の設定だけでは解決できない場合もあるので,いろいろ実験してみてください.