BEACHSIDE BLOG

MicrosoftとかC#を好むレンジャーの個人的メモ

C# で API Gateway から Lambda - プロキシ統合 の使用とか

API Gateway から Lambda を呼ぶ際の設定、Lambda プロキシ統合 の使用有無時の実装などをメモ。

(2017/10月時点での話=.NET Core 1.0しかサポートしてない時点です)

Overview

 1 開発環境の準備 (その1)
 2 .NET Core 1.0 対応の .NET Standard 1.6 のクラスライブラリの作成(その1)
 3 簡易なクラスライブラリー実装(その1)
 4 Console プロジェクトの作成(その2)
 5 Autofac の実装(その2)
 6 AWS Labmda プロジェクトの作成(その2)
 7 AWS Labmda の環境変数を読み込む
 8 API Gateway から Lambda - プロキシ統合 の使用とか(←今回ココ)
 9 AWS Lambda から AWS Lambda の呼び出し
 10 AWS Lambda から CloudWatch の呼び出し (後日)

環境は、.NET Core1.0 と .NET Standard1.6 です(雑...)といっても今回は、API Gateway 利用時の Lambda へつなぐ前後の設定が中心。

こんなAPIつくるイメージです。

  • GET メソッドで取得
  • **/api/user/{id} って感じのURLにアクセスしたら、なんらかの情報を返す(っぽいイメージ)。

API Gateway の作成

API GatewayAWS マネジメントコンソールでさくっと作れるので細かい手順は省略します。 細かい手順はAWSのドキュメント(「Lambda 関数を公開するための API を作成する」)にて♪

demo-api05」という名称で作って、 リソース: user > リソース: {id} > メソッド: GET を作成しました。こんな画面にたどり着きます。

f:id:beachside:20171017175055p:plain

全てCORSを有効にしているので、OPTIONS が自動で生成されています。


「Lambda プロキシ統合の使用」をチェックするかしないかで、Lambda 側の実装が結構かわります。

「Lambda プロキシ統合の使用」を使わないなら、
API Gateway 側で渡したい情報に対して手動でマッピングの設定をすることで、Lambda へ渡すことができます。必要な情報だけを渡してあげれば Lambda 側で受け取る情報がシンプルになりますね。

「Lambda プロキシ統合の使用」を使うと、
API に来た情報(httpのヘッダーやbodyの情報とか諸々)を AWS Lambda へ渡してくれます。で、 Lambda 内で加工する感じですね。便利な感じですが、個人的には制約がちょっとうざく感じました。

どっちがいいかとかは用途次第ですね。両方試してみましょう。

統合リクエスト > Lambda プロキシ統合 を使わない場合

デモ用 Lambda

デモ用の Lambda として以下のプログラムをデプロイしておきました(デプロイ手順はここら辺な感じ...)

API Gateway から情報受け取るのに、InputModel クラス型を定義しています。つまりこの定義に合わせて API Gateway 側でマッピングしてあげます。 出力には、OutputModel クラスを定義して、jsonを返すようにしてます。BaseDemoModel クラスを継承させて、キャメルケースでjsonを返すようにしました。

本文マッピングテンプレートの設定

API Gatewayマッピングの設定です。そもそもマッピングする元の情報について、詳しくはAWSのドキュメントを見た方がよいでしょう。

設定していきましょう。API Gateway で、対象のメソッドの「統合リクエスト」を選択します。

f:id:beachside:20171017150702p:plain


下の方にある「本文マッピングテンプレート」を開きます。

f:id:beachside:20171017150725p:plain


今回の例だと、 「Content-Type」で「application/json」と入力し、テンプレートを以下のように編集します。

f:id:beachside:20171017150945p:plain

Lambda が受け取る引数の型に合わせてテンプレートを書いています。今回だと URLの{id}InputModel クラスの UserIdマッピングさせるので、以下のようにすればOKです。

{
    "userId": "$input.params('id')"
}


あえてマッピングJsonをキャメルケースで書きましたが、LambdaSerializerにはJSON.NETが使われているので、キャメルケースのJSONパスカルケースのC#のクラスへのデシリアライズは良しなにやってくれます。

では、テストしてみましょう。API Gateway の今回のメソッドを選択して、「テスト」をクリックします。

f:id:beachside:20171017150956p:plain


{id} には、「yoko」と入力して「テスト」ボタンをクリックすると、想定通りっぽい?レスポンスが返ってきてます。

f:id:beachside:20171017151003p:plain

エスケープシーケンス、ジャマい!

「ジャマい」ってなんだろうってのはさておき、エスケープシーケンスを排除します。API Gateway の今回のGETメソッドの「統合レスポンス」を開きます。

f:id:beachside:20171017151012p:plain


既存のレスポンスのステータスが200のやーつを開き、本文マッピングテンプレートの application/json をクリックして、テンプレートを書きます。

f:id:beachside:20171017151022p:plain


書く内容はこれ。で、本文マッピングテンプレートを保存して、上の方の保存ボタンもクリックします。

$input.path("$")

では、テスト。これで想定通りのレスポンスを返すことができました。

f:id:beachside:20171017151032p:plain


統合リクエスト > Lambda プロキシ統合 を使う場合

ここはイマイチ正しいやり方を理解していない気がしますが...書いてきましょう。Lambda プロキシ統合を使う場合は、API Gateway を作るとき、または作った後「統合リクエスト」で「Lambda プロキシ統合の使用」にチェックを入れるだけです。

f:id:beachside:20171017155106p:plain

これを使う際に注意したいのは、Lambda が出力するレスポンスに制約があることです。

プロキシ統合のための Lambda 関数の出力形式

これにそわないと 502 のエラーになります。

プロキシ統合用 Lambda

レスポンスの制約を踏まえてこんなデモ用 Lambda を作りました。

ざっくり解説として、まず最後のレスポンスについてですが、制約に合わせて ProxyResponse というクラスを作りました。 これをそのままレスポンスとして返してあげればOKです。

API Gateway からのリクエストですが、 Stream で受け取って表示してみました。表示するためだけのサンプルです。CloudWatch でログで見ると具体的にみえます。

必要な情報を引っこ抜いたり必要なプロパティ持ったクラス作って、Desirializeして使えばよいですね。

テストした結果はこんな感じ。

f:id:beachside:20171017163521p:plain


上のコードでは適当に値を返していますが、bodyになんか入れたいならjsonの文字列にしてあげればよいです。プロキシ統合を使った場合は、エスケープシーケンスじゃまい問題は起きませんでした♪。

おわりに..

よくあったミスをメモ....

CORSの設定忘れ

API Gateway を作るたびに(チェックボックスポチるだけなのに)設定し忘れて、エラーを見るたびに「う...」ってなってました。ガチでしかもたくさん作るなら諸々をCLI使った方がしあわせですかね。

設定変更後のデプロイ忘れ

API Gateway の変更をステージに反映するには、デプロイ(「アクション」 の選択肢から 「APIのデプロイ」を選んでやるやつ)が必要です。ちなみにLambdaは、デプロイしてしまえばステージの方にも反映さます。なんか違和感ありますが...。

なんか事故ったときに、直したのに反映されないと これも単なる忘れごとなだけですが、「う...」ってなってました。

おしまい