21
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS CloudFront FunctionsでSPAのルーティング処理

Last updated at Posted at 2021-05-05

緊急事態宣言で暇を持て余していた2021年のGWに、AWSからナイスな機能がリリースされました。
その名も「CloudFront Functions」。CloudFrontでクライアントからのアクセス時にJavaScriptで書かれた処理をかますことができる機能です。
今回は、このCloudFront Functionsを使って、SPA特有の「すべてのアクセスをindex.htmlに向ける」処理を書いてみましたのでご紹介します。

CloudFront Functionsとは?

https://aws.amazon.com/jp/about-aws/whats-new/2021/05/cloudfront-functions/
https://aws.amazon.com/jp/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/
詳しくは以上のAWS公式サイトをご覧いただければと思いますが、要約すると「CloudFrontがリクエストをOriginに転送する前に、1ms以下の実行時間の比較的軽い処理を挟むことができる」という機能です。
同じような機能としてすでにLambda@Edgeが存在しますが、こちらはより軽量版といったところでしょうか。
実行時間が最大1msとかなり厳しく、できる処理はURL・ヘッダーの書き換えだったりとか、リダイレクトとかに限られますが、その代わり高速にレスポンスが返せます。
料金は、100万リクエスト当たり$0.10とのことです。Lambda@Edgeよりも安いですね。

index.htmlへのルーティングをCloudFront Functionsで実装してみる

React/Vue/Angular等で作られたシングルページアプリケーション(SPA)は、ルーティングの処理をブラウザで実行されるJSで行うため、サーバー側でルーティングを行う必要がありません。すべての(静的ファイルへのリクエストを除く)リクエストは、index.htmlに向けられる必要があります。でないと、/hogeのようなサブページへのルーティングが404になってしまいます。

下準備

image.png
まず、/hogeというサブページを持つAngularアプリケーションを用意しました。
私がAngularの使い手というだけなので、これはVueでもReactでも何でもいいと思います。
image.png
用意したSPAアプリケーションを、CloudFront+S3の形でアップロードして公開します。
ここまでで、<ドメイン>/直下、つまりTOPページは正しく表示されます。
しかし、アドレスバーに直接<ドメイン>/hogeと入れて、サブページを表示しようとすると、Access Deniedのエラーが返ってきます。
image.png
これは、S3がhogeという名前のファイルを返そうとしているためです。そのようなファイル実体は存在しないので、S3は404を返し、こちらに書かれているようにCloudFrontは403を返します。
なので、/hogeのようなファイル実体が存在しないパスにルーティングしようとした際は、JSでルーティング処理を行わせるために、index.htmlを返す必要があるというわけです。
ということで、ここでCloudFront Functionsの出番です。

CloudFront Functionsの設定

image.png
CloudFrontの左側メニューの中に「Functions」が追加されていますので、クリックします。
image.png
「Create Function」をクリックします。
image.png
適切な名称を入力します。
image.png
手順としては4段階、ステージは2つに分かれています。
まず「Build」。コードを書いて保存すると、「Development」ステージに反映されます。この段階では本番反映ではありません。

var indexPage = "index.html"

function handler(event) {
    console.log(event);
    var request = event.request;
    var currentUri = request.uri;
    
    var doReplace = request.method === "GET" && currentUri.indexOf(".") === -1;
    
    if(doReplace) {
        var indexPath = `/${indexPage}`;
        request.uri = indexPath;
    }
    
    return request;
}

以上のコードを入力して「Save」してください。
やっていることは、リクエストメソッドがGETかつ、URIにドットが入っていない(=拡張子がない)場合、index.htmlを返すように変更するというものです。
console.log()を含めていますが、ここで取れたログはus-east1リージョンのCloudWatch Logsに保存されます。
何やら懐かしいスタイルのJSコードを書いていますが、今のところES5相当の文法にしか対応していないようです。なんでやねん。
image.png
次に「Test」フェーズで、保存されたコードをテストできます。
今回は「Viewer Request」のフェーズで処理を挟みますので、「Event Type」をそのように選択します。
HTTPメソッド、URI、クエリパラメータ、リクエストヘッダー、Cookie、クライアントIPなどを変更できるようです。
「Compute utilization」という値は、最大実行時間の1msを100とした時の、実際の実行時間の値だそうです。最大でも70~80ぐらいに抑えておいたほうがよさそう。
image.png
このように、InputのURIに/hogeを入力して、OutputのURIが/index.htmlになっていればOKです。
image.png
次に「Publish」をして、この段階で「Live」ステージにもコードがデプロイされます。
image.png
最後に「Associate」で、このFunctionを紐づけたいCloudFrontディストリビューションを設定します。
先ほども書いたように、「Viewer Request」のフェーズで処理を挟むので、「Event Type」は「Viewer Request」を選んでください。
ディストリビューションの選択時、自動補完が出てきますが、ディストリビューションIDしか出てこないので、事前に紐づけたいディストリビューションのIDをメモっておいたほうがいいです。
image.png
設定が完了すると、/hogeをアドレスバーに直入力しても、正常にサブページが表示されるようになります。

CloudFront Functionsで出来ないこと

CloudFront Functionsは、Lambda@Edgeで出来ることをもっとそぎ落として軽量高速化した機能であるため、今のところ以下のようなことはできません。

  • コード内で別のAWSリソースへのアクセスはできない
  • 最大実行時間が1msを超えるような重い処理はできない
  • 「Origin Request」「Origin Response」イベントタイプの処理はできない

このような処理を行いたい場合は、Lambda@Edgeを採用するとよいでしょう。

まとめ

従来、SPAのindex.htmlへのルーティング処理をCloudFrontで行う場合、「Custom Error Response」でオリジンが403を返した時に、ドメイン直下(index.html)を返すように設定するというような、若干ハッキーな手法が多く用いられてきました。その後、Lambda@Edgeが出てきて一応解消できるようにはなりましたが、デプロイまでが結構複雑なので二の足を踏んでいた方も多いと思います。今回のCloudFront Functionsの登場で、よりシンプルに同様の処理を実装できるようになりました。ありがとうAWSさん。

21
11
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
21
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?