SPA（S3+Cloud Front）でLambda@Edgeを使ってTwitterカードを表示する〜コンソールOnly

Twitterカードに代表されるOGPの実装は、基本的にmetaタグに情報を加えるだけ。
ただ、React等SPAとして実装されたサイトでOGPを実装する場合は一工夫必要。

# OGPの基本的な仕組み
1. ユーザーがSNS(Twitterなど)にリンクを貼る
2. 各SNSのボットがそのURLにアクセスして、metaタグを読みにいく

この時、ボットがJSを実行できないために、
SPAの場合は常にルートのindex.htmlのmetaタグが読み込まれてしまう
＝表示される画像などを動的に変更することができない

# 指針
1. CloudFrontのビューワーリクエストにLambda@Edgeを差し込む
2. UserAgentを判定し、botからのアクセスの時だけ、動的にmetaタグを追加したHTMLをレスポンスとして返す

ユーザーがSNSにリンクを貼ってボットがサイトに来たときだけ、表示させたい画像をmetaタグに詰めて渡してあげる
# 実装
## Lambda@Edgeの実装
Lambda@Edgeはus-east1(バージニア北部)リージョンでしか使用できないため、
それ以外のリージョンにいる場合は切り替えてから関数を作成する。

コードは https://qiita.com/kurimoto/items/6212372ead6522161a60 を参考にした。
ユーザーからのリクエストを読み取って、UserAgentを判定し、Twitterbot等の場合だけ作成したレスポンスを返す。

```js:index.js
'use strict';

const bots = ['Twitterbot'];

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  const userAgent = request.headers['user-agent'][0].value;
  const isBot = bots.some(v => {
    return userAgent.includes(v);
  });
  if (isBot) {
    const body = getContent()
    const response = {
      status: '200',
      statusDescription: 'OK',
      headers: {
        'content-type': [{
          key: 'Content-Type',
          value: 'text/html'
        }]
      },
      body: body
    };
    callback(null, response);
  } else {
    callback(null, request);
  }
};

const getContent = () => { 
 return = `
  <!doctype html>
  <html lang="ja">
  <head>
  <meta charset="utf-8" />
  <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
  <meta content="width=device-width, initial-scale=1.0" name="viewport" />
  <meta content="R-bies,INC." name="author" />
  <title>タイトル</title>
  <meta name="twitter:card" content="summary" />
  <meta property="og:url" content="http://example.com/" />
  <meta property="og:title" content="Example" />
  <meta property="og:image" content="${imageUrl}" />
  </head>
  <body>
  </body>
  </html>
  `;
};
```
上のコードはそのまま動かない。（metaタグに設定しているimageUrlが定義されていないから）

UserAgentを判定する
あらかじめ指定したボットだった場合は動的にタグを追加してレスポンスとして返す。
それ以外はリクエストをそのまま実行する（S3にアクセスする）

というものになっている。
ので、動的に画像を差し替えたりしたいときは、条件を追加したり、URLに応じてDBからfetchしてきたりして、imageUrlを定義してあげればよい。

### 参考
CloudFront経由のイベントは下記のフォーマットで渡ってくるので、
テストをする際は下記をコピペしてあげたり、イイ感じに改変すればよい

```json
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Twitterbot"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/example/5674591867371520"
        }
      }
    }
  ]
}
```

## Lambda@Edgeへのデプロイ

関数を作成してテストも通ったら、Lambda@Edgeへデプロイする。

関数設定のトリガーを追加するところから、CloudFrontを選択
<img width="812" alt="スクリーンショット 2020-03-29 22.56.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248316/bf737ed2-2b16-b118-5de0-d07d11582046.png">

デプロイするディストリビューションを設定
イベントをビューアーリクエストに設定
<img width="963" alt="スクリーンショット 2020-03-29 22.58.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248316/8aedddbf-4644-273b-e43d-0c1fed7ff856.png">

追加する

## Lambdaの実行ロールの設定
開く
<img width="552" alt="スクリーンショット 2020-03-29 23.01.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248316/e1b03ec1-40a4-3875-41b0-cd760683bcf9.png">

信頼関係を編集する
<img width="410" alt="スクリーンショット 2020-03-29 23.02.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248316/26b6a5a6-3868-7858-cd27-22c564f7123b.png">

下記を貼る

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "edgelambda.amazonaws.com",
          "lambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```


## CloudFrontの設定
コレをしないと延々５０３が出て困る

デプロイしたディストリビューションのbehaviorを追加する
<img width="528" alt="スクリーンショット 2020-03-29 23.03.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248316/d1cdd608-65b6-a1cc-99b4-88dccd7fcafc.png">

Lambdaを実行するパスのパターンを設定する。全てで動かす場合は *
<img width="703" alt="スクリーンショット 2020-03-29 23.04.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248316/ca31f473-d208-1a9e-5c95-be957f6acde0.png">

実行させるLambdを設定する
<img width="1027" alt="スクリーンショット 2020-03-29 23.05.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248316/c21dc58f-2a3e-83d3-e608-c4807ef5d127.png">
ARNはLambda画面の右上にある
<img width="867" alt="スクリーンショット 2020-03-29 23.07.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248316/dd8ce6b0-c96d-4017-fade-f64e56ba91db.png">

これで追加すると、CloudFrontにデプロイされ、
晴れてOGPタグを動的に差し替えることに成功する