API Gateway + DynamoDB のみで JSON を受け取る

AWSTech

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.requestIdtest-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 内容

パースされた格納結果


以上です。ツッコミ等々ありましたら、ぜひコメント等よろしくお願いします。

AWSTech