
はじめに
こんにちは~、BFT名古屋支店の猫です。
毎年秋になると"焼き芋",
"安納芋"などと書かれたお菓子を買いあさるのですが、今年はカントリーマアムの紅天使味にドハマりしています。みなさん見かけたら買うべし。
前回の「RESTful API 作ってみた!」という記事で、AWSの API Gateway、Lambda、RDS を使ったRESTful API の作り方をご紹介しました。
今回は、この中でも特に苦労したプロキシ統合の使い方についてまとめていきたいと思います。
という方の参考になればと思います。
▼前回の記事はこちら
プロキシ統合とは
API GatewayからLambdaを呼び出す際、デフォルトではリクエストパラメータの受け渡しは行われません。
Lambda側でリクエストパラメータを知りたい場合、2つの実装方法があります。
1つめはAPI Gatewayでマッピングテンプレートというものを設定する方法です。
マッピングテンプレートでは、リクエストからどのパラメータを抽出し何という変数名でLambdaに渡すかを定義します。Lambdaに送る情報を制限することができる点はメリットですが、API GatewayとLambdaそれぞれで実装する必要があるため実装や管理が煩雑になります。
2つめの方法、プロキシ統合ではこのデメリットを解消することができます。
プロキシ統合とは「API Gatewayでマッピングをしなくても、リクエストに入っているパラメータをまるっとLambdaに渡してくれる機能」です。
リクエストパラメータを自動でいい感じに変数に詰めてLambdaに渡してくれるため、実装や管理の煩雑さが軽減されます。
デメリットとしては、Lambdaに送る情報を制限することができない点が挙げられます。
上記で2つの方法をご紹介しましたが、そもそもリクエストからパラメータを抽出する必要がない場合はどちらも不要です。
つまり、リクエストパラメータの受け渡しという観点でみると、
- リクエストからパラメータを抽出する必要がない場合
⇒ プロキシ統合を使用しない(マッピングテンプレートも使用しない) - リクエストからパラメータを抽出する必要があり、その処理をAPI Gatewayで実装したい場合
⇒ プロキシ統合を使用しない(マッピングテンプレートを使用する) - リクエストからパラメータを抽出する必要があり、その処理をLambdaで実装したい場合
⇒ プロキシ統合を使用する
と考えればよさそうです。
プロキシ統合使うとき/使わないときの値のやり取り
ここでは実際にプロキシ統合を使うとき / 使わないときの、API Gateway・Lambdaの設定値やコードについて記載します。
以下のリクエスト例のように、リクエストからパスパラメータとbodyを抽出したい場合を想定しています。
- リクエスト例
- URL
https://example.execute-api.ap-northeast-1.amazonaws.com/my-recipe-prod/{id}
例)
https://example.execute-api.ap-northeast-1.amazonaws.com/my-recipe-prod/3
- メソッド:PATCH
- body の中身
- URL
{ "title": "チキンカレー", "making_time": "45分", "serves": "4人", "ingredients": "トマト,玉ねぎ,肉,スパイス", "cost": "1000" }
プロキシ統合使わないとき
リクエストからパラメータを抽出する処理をAPI Gatewayで実装したい場合、API Gatewayで「プロキシ統合の使用」を設定せずにLambda統合を使用することで実現できます(非プロキシ統合やカスタム統合とも呼ばれます)。
この場合、リクエスト内の取り出したいパラメータをAPI Gatewayのマッピングテンプレートで指定する必要があります。 マッピングテンプレートの書き方については、以下サイトの「$input 変数」「$input 変数テンプレートの例」部分を参照してください。
{ "id" : "$input.params('id')", "body" : $input.json('$') }
- Lambda の中身
import json def lambda_handler(event, context): ''' 入力受け取りの書き方 # API Gatewayで定義したパラメータがevent変数の中に詰められて渡ってくる ''' req_id = int(event['id']) # そのまま取り出すと文字列型になってしまうので、数値として使用する場合はint型に変換する req = event['body'] # dict形で受け取れる ''' レスポンスの書き方 # レスポンスbodyを返してあげればよい ''' responce = { "message": "Success!", "req_id": req_id } return responce
プロキシ統合使うとき
リクエストからパラメータを抽出する処理をLambda内で実装したい場合、API Gatewayで「プロキシ統合の使用」を設定することで実現できます。
統合プロキシを使用する場合、レスポンスの書式がある程度決まっているので注意が必要です。
詳しくは以下サイトの「プロキシ統合のための Lambda 関数の出力形式」部分を参照してください。
docs.aws.amazon.com
import json def lambda_handler(event, context): ''' 入力受け取りの書き方 # 全てのパラメータがevent変数の中に詰められて入ってくる ''' req_id = int(event['pathParameters']['id']) # そのまま取り出すと文字列型になってしまうので、数値として使用する場合はint型に変換する req = json.loads(event['body']) # json.loadsを用いることでdict型で読み込める ''' レスポンスの書き方 # ステータスコードやヘッダーも付けて返す必要がある ''' responce = { "isBase64Encoded": True, "statusCode": 200, "headers": {"MyHeader": "MyHeaderVal"}, "body": "my body" } return responce
※注意:body部分にjson形式でデータを入れたい場合
body部分に直接dict形式を挿入するとエラーがでます。エスケープ処理が必要です。
''' レスポンスの書き方 ''' res_body = { "message": "Success!", "id": req_id } responce = { "isBase64Encoded": True, "statusCode": 200, "headers": {"MyHeader": "MyHeaderVal"}, "body": json.dumps(res_body) # エスケープ処理 } return responce
おわりに
こうしてまとめると、なにをドタバタ大騒ぎしていたのか?と思うくらいの内容ですが 、分からないときって本当に何も分からないんですよね~(遠い目)
この記事を読んでいただいた方はすんなり実装できるといいなと思います。
今回は同じAPIの中でメソッドごとにプロキシ統合を使ったり使わなかったりといった実装にしてしまったため、引数の型が違ったりレスポンスの返し方が違ったりしてあんまりきれいじゃないコードになってしまいました。
他の人からの見やすさ、コードの移植性などを考えると、設計段階でどちらかに統一したほうがよさそうですね。
ここまで読んでいただきありがとうございました^^