APIGatewayを使ってサーバーレスなURL短縮サービスを作る（URL取得編）

# 何を作るか？

諸事情により、外部のURL短縮サービスを使えない場合のために、自前のURL短縮サービスを作ります。
このサービスは、以下の2つから構成されます。

* URL生成
    * 入力された任意のURLに対して、短縮したURLを生成する
    * 生成したURLを元のURLと共にAmazon S3に格納する
* URL取得
    * 与えられた短縮URLを元にS3から元URLを取得する
    * HTTPレスポンスでステータスコード301とともにLocationヘッダで元URLを返す

この記事では、Amazon APIGatewayを使ってURL取得の部分を実装します。
URL生成の部分は[こちらの記事です](https://qiita.com/monamu/items/1f02f1b8084d2772cb94)

# 概要

構成はシンプルにAPIGatewayとS3、あとはそれに関わるIAMRoleのみです。

![fig1](https://docs.google.com/drawings/d/e/2PACX-1vRlKc-yj4egUQfroV7XeAR0r1PkvtsWo-4fRfSJ8vsVPMfixWsJX11oRfMCQS7Jf4JpBd55WMgJF5Qk/pub?w=960&h=720)

1. クライアント（Webブラウザ）は、短縮URLの https: //API-ENDPOINT/hoge にアクセスする。
2. APIGatewayはS3バケットにオブジェクト"hoge"を要求する。
3. APIGatewayはS3から取得したオブジェクトの中のLocationプロパティをクライアントへのレスポンスのLocationヘッダに設定する。
4. APIGatewayはStatusCodeを301としてクライアントにレスポンスを返す。

短縮したキーと、元のURLの対応付をS3オブジェクトのKeyとBody（の中のLocation）で持たせています。

API-ENDPOINTは、デフォルトではAPIGatewayのURL(RESTApi.execute-api.REGION.amazonaws.com/STAGE)になってしまい、短縮URLとしては長すぎるので、短めのドメインを取得して、APIGatewayにカスタムドメイン名を設定してください。
（この記事ではカスタムドメイン名の設定については触れません）



# 作成方法

CloudFormationのテンプレートを用意してあるので、まずはそれを実行します。

https://github.com/monamu/qiita-url-shorter

こちらからapp-geturl.yamlを取得して、CloudFormationで実行してください。
パラメータ無し、StackNameは任意で構いません。

# 動作確認

CloudFormationからS3バケットが作成されています。S3バケットに以下のファイルを`hoge`という名前でアップロードしてください。

``` JSON:hoge
{
  "Location": "https://www.google.com"
}
```

CloudFormationのOutputsに"Api"の値が出力されているので、`{key}`の部分を`hoge`に書き換えてWebブラウザからアクセスしてください。

![https://docs.google.com/drawings/d/e/2PACX-1vSla7kQAbTIlDrC_0ubm9VUCxEnBFf1RliLs7gEwUp0DOSJ74jDQ3E-rbz_MVrbJPKEkSoV83r6trQw/pub?w=584&h=359](https://docs.google.com/drawings/d/e/2PACX-1vSla7kQAbTIlDrC_0ubm9VUCxEnBFf1RliLs7gEwUp0DOSJ74jDQ3E-rbz_MVrbJPKEkSoV83r6trQw/pub?w=584&h=359)

https://www.google.com にリダイレクトされていれば成功です。

# 解説

APIGatewayで何を行っているかを説明します。CloudFormationで既に作成されているものを確認してください。

一言で言うと「APIGatewayをS3プロキシとして構成して、レスポンスヘッダにS3オブジェクトの内容からマッピングしている」ということになります。

まずは、APIGatewayをS3プロキシとして構成する方法ですが、APIGatewayのGETメソッドの統合リクエストを以下のように構成しています。

![](https://docs.google.com/drawings/d/e/2PACX-1vSyZ34pLvNW_wPvlNyyjEhHOvfFG2nbtn9h228nCtWn0hZ-6go6n1ejNBol-H6x4TRPMGytb5-niCmM/pub?w=631&h=582)

1. パスパラメータとして`{key}`を定義し
2. S3へのリクエストパスが`バケット名/{key}`となるようにして
3. S3にGETメソッドを発行する

という感じです。こうすると、APIGatewayへのGETリクエストがそのままS3へと流れます。

詳細はこちら → Amazon S3 プロキシとして API を作成するhttps://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/integrating-api-with-aws-services-s3.html

続いて、S3オブジェクトの内容をレスポンスにマッピングします。

まずは、APIGatewayからのレスポンスをWebブラウザがリダイレクトするように、APIGatewayのレスポンスを定義しています。

![](https://docs.google.com/drawings/d/e/2PACX-1vSfuBtQABsJGWKTWU4d-HFjflCiZAJkLhzZr04JAkoGy5ROrsppv3IhHdFoXW0kZZ5eAUmt6Hsm_HyS/pub?w=371&h=237)

次に、APIGatewayのレスポンスに、S3から返ってきたオブジェクトの内容をマッピングしています。

![](https://docs.google.com/drawings/d/e/2PACX-1vQUNFhFqZOvWC2oUrh2neGbVf3oB8yGDLh-KKPEfMsKzwethMgKf6NqsfxUCyeFkkg1_DO9_qnMNgsQ/pub?w=418&h=374)

* S3からのHTTPステータスが2xxだったらAPIGatewayが301を返す
* APIGatewayのレスポンスヘッダー`Location`に、S3からのレスポンスBodyの`Location`プロパティを設定する

# まとめ

APIGatewayのS3プロキシとマッピング定義でプログラミングなしで、URL取得部分が実装できました。
あとは、S3バケットにオリジナルURLと短縮後のキーを格納する処理を実装するだけなので、任意のプログラムで実装していただければと思いますが、別記事でAWS Lambdaでの実装例を書く予定です。

# おまけ

短縮URLに有効期限をつけたい場合、S3バケットのライフサイクルルールでオブジェクトを失効させることで、これもプログラミング無しで実現できます。