yuu26's memo

試したことや調べたことなどを記録しています。まずは継続的な更新が目標です。

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

概要

API Gateway で受け取ったデータをマッピングテンプレートで変換し、DynamoDB が理解できる形式にして直接投げ込みます。Lambda は使用しません。

f:id:yuu2634:20171013220015p:plain

AWS アイコンの構成図、一度くらい載せたくて作ったのはいいですが、何となくコレジャナイ感が。
流行りのサーバレスです。


CSP(Content-Security-Policy) のレポートを受け取る

ここは余談です。
今回は、CSP レポートを受け取るために環境を構築しました。

CSP とは、サイトのセキュリティポリシーを設定する機能で、XSS 軽減や Mixed Content 検出などに役立ちます。詳しくは Google 先生へ


CSP では、ポリシー違反が発生した際に ブラウザが違反内容を報告する機能 があり、予め指定しておいた URL に違反内容が json 形式で POST されます。


その POST を受け取るために Web サーバが必要なのですが、今回は API Gateway + DynamoDB のみで CSP レポート(json)を受け取ります。


DynamoDB テーブル作成

まずは json の格納先として、DynamoDB のテーブルを作成します。プライマリキーを id 列として作成しました。

f:id:yuu2634:20171013220903p:plain


IAM ポリシー作成

DynamoDB にアクセスできる IAM ポリシーを作成します。今回は、Policy Generator で PutItem のみ許可しました。

f:id:yuu2634:20171010235130p:plain


IAM ロール作成

API Gateway 用のロールを作成します。中身はデフォルト、名前は適当です。

f:id:yuu2634:20171010235318p:plain

作成後に、先ほど用意した IAM ポリシーを追加でアタッチします。

f:id:yuu2634:20171010235340p:plain

ここで作成した IAM ロールの ARN をメモしておきます。


API Gateway 作成

API Gateway を新規作成します。例によって名前は適当。

f:id:yuu2634:20171011000110p:plain

アクションメソッドの追加 から POST メソッドの処理を追加します。

f:id:yuu2634:20171011000134p:plain

以下のように設定して保存します。

  • 統合タイプ: AWS サービス
  • AWS リージョン: 使用するリージョン
  • AWS サービス: DynamoDB
  • HTTP メソッド: POST
  • アクション: PutItem
  • 実行ロール: メモした IAM ロールの ARN

f:id:yuu2634:20171011000524p:plain


マッピングテンプレート作成

DynamoDB へ登録するために、リクエストの形式を変換します。

統合リクエスト を再度クリックします。

f:id:yuu2634:20171011001522p:plain


最下部にある 本文マッピングテンプレート を設定します。

  • リクエスト本文のパススルー: なし
  • Content-Type: application/json を追加
    ※今回は、CSP レポート受信のため application/csp-report も追加(設定内容は同一)

f:id:yuu2634:20171013224436p:plain

使用しているコードは以下の通りです。フォーマットは 公式ドキュメント 参照。

#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 メソッドの画面に戻り、左上の テスト ボタンをクリックします。

f:id:yuu2634:20171011001522p:plain


最下部に、任意のリクエスト内容を入力できます。ここでは、CSP レポートの例 をそのまま入れてみます。

f:id:yuu2634:20171011004240p:plain


成功したようです。

f:id:yuu2634:20171013221405p:plain

DynamoDB を確認してみると、無事に JSON が格納されていました。

f:id:yuu2634:20171013221843p:plain


【注意点】
上記画像をよく見ると分かるのですが、テスト実行では $context.requestIdtest-invoke-request という固定文字列が入ります。

本来はここにランダムな ID が入るため問題ありませんが、テスト実行では毎回この値となるため、連続でテスト実行するとプライマリキー重複でエラーとなります。


API のデプロイ

動作確認が取れたのでデプロイします。ここからは通常の API Gateway と同じ手順です。

f:id:yuu2634:20171013224824p:plain f:id:yuu2634:20171013222653p:plain


デプロイできました。

f:id:yuu2634:20171013224956p:plain

ここで発行された 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 内容

f:id:yuu2634:20171011004240p:plain


格納結果

f:id:yuu2634:20171013225115p:plain


以上。ツッコミ等々あればご連絡いただけるとありがたいです。