背景と目的
APIGatewayとLambdaを一緒に使う場合、設定項目がたくさん用意されていて自由度は高いのだが、いろいろありすぎてどんな時に何をすればいいかわからなくなる。公式ドキュメントを読まずともある程度のことが分かるようにメモしておく。
前提
- APIGatewayに入ってきたリクエストを、APIGateway>統合リクエスト>マッピングテンプレートを用いてJSONに成型して、Lambdaの引数eventに引き渡す
APIGateway
設定
バイナリメディアタイプ
バイナリとして受け入れるContent-Typeを設定する。たとえば、application/octet-streamやimage/jpegなど。設定しておかないと、Unsupported Media Typeというレスポンスが帰ってきてしまう。ここで設定したら、統合リクエスト>マッピングテンプレートの$input.bodyにbase64エンコードされた文字列が入るようになる。
リソース
メソッドリクエスト
編集中
統合リクエスト
HTTPヘッダー
- Lambdaの引数event.params.headerに渡すデータの設定ができる。
- クエリパラメータ、パスパラメータ、リクエストヘッダーなどいろいろなものをマッピング可能。
リクエストのパススルー
- そのままだと、リクエストボディがJSONであることを期待して、jsonパースしてLambdaの引数event.body-jsonに入れる。JSONパースできない場合、Lambdaに到達する前に怒られる。
Content-Typeがtext/plainの場合
以下のようにすれば文字列を入れられる。
"body-json": "$input.body"
Content-Typeがapplication/octet-streamなどのバイナリの場合
以下のようにすれば、base64エンコードされた文字列がLambdaに渡される。もちろん、先述のバイナリメディアタイプの設定が事前にされている必要がある。
"body-json": "$input.body"
統合レスポンス
統合レスポンスは、Lambdaからのレスポンス内容に応じて、メソッドレスポンスに伝える形式を定義する部分。おそらくエラー時の整形の時に一番気を使うと思われるので、エラー処理に絞ってメモする。
Lambda エラーの正規表現
まず、PythonランタイムのLambdaで、エラーが起きた時や以下のようにわざとエラーを起こしてみると、
raise Exception("設定した文字列")
Lambdaからのレスポンスとして、統合レスポンスに以下のようなJSONが伝わる。(自前でカスタム定義することも可能だが、特に何もしなければ)
{
"errorMessage": "設定した文字列",
"errorType": "エラータイプ",
"stackTrace": [
スタックトレース,
:
]
}
- Lambda エラーの正規表現は、errorMessageの中の文字列を捕まえる条件。この条件に当てはまる場合に、メソッドレスポンスのどのステータスコードに割り当てるかも設定する。
Lambda エラーの正規表現で捕まえたときに、適用される。
基本形式として、Empty、Errorが定義されているので、これを少し改造することで好きなものが作れる。
空のJSON
#set($inputRoot = $input.path('$'))
{}
メッセージだけ。
#set($inputRoot = $input.path('$'))
{
"message" : "foo"
}
$inputRootは、先述のLambdaからのエラー時のレスポンスが入っているので、
{
"message" : "$inputRoot.errorMessage"
}
などとすれば、エラーメッセージだけ取り出せる。
メソッドレスポンス
編集中
Lambda
引数event
マッピングテンプレートの生成 = リクエストのパススルーのとき
{
"body-json": "{ボディ}",
"params": {
"path": {
"pathParameter": "value",
:
},
"querystring": {
"queryParameter": "value",
:
},
"header": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Cache-Control": "no-cache",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Content-Type": "{Content-Type}",
"Host": "{api-id}.execute-api.ap-northeast-1.amazonaws.com",
"User-Agent": "PostmanRuntime/7.29.0",
"Via": "1.1 22512dca1de1fae848b2509fed0309aa.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "FXfKr5sbNKhgGGfyTwehKew20UNqXsoPp7xyDL-y8TqgAkwCnWr94w==",
"X-Amzn-Trace-Id": "Root=1-6215f7c0-12c8260b2b11d6fe7f74d914",
"X-Forwarded-For": "54.86.50.139, 130.176.137.78",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
}
},
"stage-variables": {
"type": "object",
"description": "ステージ変数"
},
"context": {
"account-id": "",
"api-id": "{api-id}",
"api-key": "",
"authorizer-principal-id": "",
"caller": "",
"cognito-authentication-provider": "",
"cognito-authentication-type": "",
"cognito-identity-id": "",
"cognito-identity-pool-id": "",
"http-method": "POST",
"stage": "{デプロイ先ステージ}",
"source-ip": "{アクセス元IPアドレス}",
"user": "",
"user-agent": "********************",
"user-arn": "",
"request-id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"resource-id": "{リソースID}",
"resource-path": "{リソースパス}"
}
}
- リクエストヘッダーは、何もしなければそのままここに入る。
- APIGateway>統合リクエスト>HTTPヘッダーで対応を定義すると、別のヘッダー名に変えて追加することもできる。
- クエリパラメータや、
LambdaでCognitoユーザーの情報を取り出す
APIGatewayのオーソライザーをCognitoのユーザープールと連携させた場合、Authorizationヘッダに入っているトークンからCognitoのユーザー情報を取り出すことができる。※event["context"]["user"]などは空になっているので取り出せないので、この方法で取り出すことになる。
authorization = event["params"]["header"]["Authorization"].split(".")
paddingLength = len(authorization[1]) % 4
for _ in range(paddingLength):
authorization[1] += "="
cognitoData = json.loads(base64.b64decode(authorization[1]).decode("utf-8"))
print(f'email={cognitoData["email"]}, username={cognitoData["cognito:username"]}')
event.body-json
event.stage-variables
APIGatewayのステージ変数で定義した値が入る。
{
"stage-variables": {
"abc": "value",
:
}
}
CORS
Lambdaプロキシ統合のとき
APIGatewayの統合レスポンスをいじれないので、Lambdaの戻り値に以下を付加する。
{'headers':
{
'Access-Control-Allow-Origin': '*'
}
}