API Gateway + DynamoDB のみで JSON を受け取る
API Gateway で受け取ったデータをマッピングテンプレートで変換し、DynamoDB が理解できる形式にして直接投げ込みます。Lambda は使用しません。
AWS アイコンの構成図、一度くらい載せたくて作ったのはいいですが、何となくコレジャナイ感が。
CSP(Content-Security-Policy) のレポートを受け取る
ここは余談です。
今回は、CSP レポートを受け取るために環境を構築しました。
CSP とは、サイトのセキュリティポリシーを設定する機能で、XSS 軽減や Mixed Content 検出などに役立ちます。詳しくは Google 先生へ
CSP ではポリシー違反の発生時にブラウザから違反内容を報告させる機能があり、指定した URL に json 形式で POST されます。今回は違反報告を API Gateway + DynamoDB で受け取りました。
DynamoDB テーブル作成
まずは json の格納先として、DynamoDB のテーブルを作成します。プライマリキーを id
列として作成しました。
IAM ポリシー作成
DynamoDB にアクセスできる IAM ポリシーを作成します。今回は、Policy Generator で PutItem
のみ許可しました。
IAM ロール作成
API Gateway 用のロールを作成します。中身はデフォルト、名前は適当です。
作成後に、先ほど用意した IAM ポリシーを追加でアタッチします。
ここで作成した IAM ロールの ARN をメモしておきます。
API Gateway 作成
API Gateway を新規作成します。例によって名前は適当。
アクション
→ メソッドの追加
から POST メソッドの処理を追加します。
以下のように設定して保存します。
- 統合タイプ:
AWS サービス
- AWS リージョン:
使用するリージョン
- AWS サービス:
DynamoDB
- HTTP メソッド:
POST
- アクション:
PutItem
- 実行ロール:
メモした IAM ロールの ARN
マッピングテンプレート作成
DynamoDB へ登録するために、リクエストの形式を変換します。
統合リクエスト
を再度クリックします。
最下部にある 本文マッピングテンプレート
を設定します。
- リクエスト本文のパススルー:
なし
- Content-Type:
application/json
を追加
※今回は、CSP レポート受信のため application/csp-report
も追加(設定内容は同一)
使用しているコードは以下の通りです。フォーマットは公式ドキュメント参照。
#set($inputRoot = $input.path('$'))
{
"TableName": "<DynamoDB テーブル名>",
"Item": {
"id": {
"S": "$context.requestId"
},
"body": {
"S": "$inputRoot"
}
}
}
API Gateway が自動生成するリクエスト UUID ($context.requestId
) を、そのまま DynamoDB の id カラムとして使用しています。
POST されてきた JSON は、$inputRoot
に格納されています。
今回は body というカラム名で丸ごと突っ込んでいます。JSON から必要な属性だけを抜き出すこともできますが、それは後ほど。
テスト実行
POST メソッドの画面に戻り、左上の テスト
ボタンをクリックします。
最下部に、任意のリクエスト内容を入力できます。ここでは、CSP レポートの例をそのまま入れてみます。
成功したようです。
DynamoDB を確認してみると、無事に JSON が格納されていました……が、文字列として格納されるのがイケてないですね。対応は 応用編 で。
【注意点】
上記画像をよく見ると分かるのですが、テスト実行では $context.requestId
に test-invoke-request
という固定文字列が入ります。
本来はここにランダムな ID が入るため問題ありませんが、テスト実行では毎回この値となるため、連続でテスト実行するとプライマリキー重複でエラーとなります。
API のデプロイ
動作確認が取れたのでデプロイします。ここからは通常の API Gateway と同じ手順です。
デプロイできました。
ここで発行された URL に JSON を POST すると、先ほどの DynamoDB に格納されます。あとは思う存分データを投げつけましょう。
応用編
JSON の中で欲しい属性が定まっている場合は、例えば以下のように定義すると幸せになれます。
マッピングテンプレート
#set($inputRoot = $input.path('$'))
{
"TableName": "csp-report",
"Item": {
"id": {
"S": "$context.requestId"
},
"document-uri": {
"S": "$inputRoot.csp-report.document-uri"
},
"blocked-uri": {
"S": "$inputRoot.csp-report.blocked-uri"
}
}
}
POST 内容
パースされた格納結果
以上です。ツッコミ等々ありましたら、ぜひコメント等よろしくお願いします。