API GatewayでAWS Lambdaを使用する
1概要
AWS Lambda
は、アマゾンウェブサービスが提供するサーバーレスコンピューティングサービスです。
/java-aws-lambda[Javaを使用してAWS Lambda関数を作成する方法]および
Lambda関数からDynamoDBにアクセスする方法
。
このチュートリアルでは、http://aws.amazon.com/api-gateway/[AWG Gateway]を使用して、
Lambda関数をRESTエンドポイントとして公開する方法
について説明します。
次のトピックについて詳しく説明します。
-
API Gatewayの基本概念と用語
-
Lambda Proxyを使用したLambda関数とAPI Gatewayの統合
統合
** APIの作成、その構造、およびAPIリソースのマッピング方法
ラムダ関数へ
** APIのデプロイとテスト
2基本と用語
API Gatewayは、開発者があらゆる規模のAPIを作成、公開、保守、監視、および保護することを可能にする** 完全に管理されたサービスです。
Lambda関数などのバックエンドサービス、その他のAWSサービス(例:EC2、S3、DynamoDB)、および任意のHTTPエンドポイント
にアクセスするための、一貫性がありスケーラブルなHTTPベースのプログラミングインターフェイス(RESTfulサービスとも呼ばれます)
を実装できます。
機能には以下が含まれますが、これらに限定されません。
-
交通管理
-
認証とアクセス制御
-
モニタリング
-
APIバージョン管理
-
攻撃を防ぐためのリクエストの調整
AWS Lambdaと同様に、API Gatewayは自動的にスケールアウトされ、API呼び出しごとに請求されます。
詳細な情報はhttps://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html[公式文書]にあります。
2.1. 条項
API Gateway
は、バックエンドHTTPエンドポイント、AWS Lambda関数、およびその他のAWSサービスを公開するためのRESTfulアプリケーションプログラミングインターフェイスの作成、デプロイ、および管理をサポートするAWSサービスです。
API Gateway API
は、Lambda関数、他のAWSサービス、またはバックエンドのHTTPエンドポイントと統合できるリソースとメソッドの集まりです。 APIは、API構造を形成するリソースで構成されています。各APIリソースは、固有のHTTP動詞を持つ必要がある1つ以上のAPIメソッドを公開できます。
APIを公開するには、
APIデプロイメント
を作成し、それをいわゆる
stage
と関連付ける必要があります。ステージはAPIの時代のスナップショットのようなものです。 APIを再デプロイする場合は、既存のステージを更新するか新しいステージを作成することができます。そのため、
dev
ステージ、
test
ステージ、さらには
v1
、
v2
などの複数の製品バージョンなど、異なるバージョンのAPIを同時に使用できます。
Lambda Proxy integration__は、Lambda関数とAPI Gateway間の統合のための単純化された設定です。
API Gatewayは、リクエスト全体をバックエンドLambda関数への入力として送信します。レスポンス的には、API GatewayはLambda関数の出力をフロントエンドのHTTPレスポンスに変換します。
3依存関係
リンクと同じ依存関係が必要です。/aws-lambda-dynamodb-java[AWS Lambda Using DynamoDB With Java]の記事。
その上、https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22com.googlecode.json-simple%22[JSON Simple]ライブラリも必要です。
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
4ラムダ関数の開発とデプロイ
このセクションでは、Lambda関数をJavaで開発および構築し、それをAWSコンソールを使用してデプロイし、そしてクイックテストを実行します。
API GatewayとLambdaを統合する基本的な機能を実証したいので、2つの機能を作成します。
-
機能1:
PUTメソッドを使ってAPIからペイロードを受け取る -
2機能2:** HTTPパスパラメータまたはHTTPの使い方
APIから取得したクエリパラメータ
実装面では、各関数に1つずつ、合計2つのメソッドを持つ
RequestHandler
クラスを1つ作成します。
4.1. モデル
実際のリクエストハンドラを実装する前に、データモデルを見てみましょう。
public class Person {
private int id;
private String name;
public Person(String json) {
Gson gson = new Gson();
Person request = gson.fromJson(json, Person.class);
this.id = request.getId();
this.name = request.getName();
}
public String toString() {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
return gson.toJson(this);
}
//getters and setters
}
私たちのモデルは二つのプロパティを持つ一つの単純な
Person
クラスから成ります。唯一の注目すべき部分は、JSON Stringを受け入れる
Person(String)
コンストラクターです。
4.2. RequestHandlerクラスの実装
リンク:/java-aws-lambda#handler[AWS Lambda With Java]の記事のように、
RequestStreamHandler
インターフェースの実装を作成します。
public class APIDemoHandler implements RequestStreamHandler {
private static final String DYNAMODB__TABLE__NAME = System.getenv("TABLE__NAME");
@Override
public void handleRequest(
InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
//implementation
}
public void handleGetByParam(
InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
//implementation
}
}
ご覧のとおり、
RequestStreamHander
インターフェースは
handeRequest()
メソッドを1つだけ定義しています。とにかく、ここで行ったように、同じクラス内でさらに関数を定義することができます。もう1つの選択肢は、各機能に対して
RequestStreamHander
の実装を1つ作成することです。
私たちの特定のケースでは、単純化するために前者を選びました。ただし、パフォーマンスやコードの保守性などの要素を考慮して、ケースバイケースで選択する必要があります。
__TABLE
NAME ____environment変数からDynamoDBテーブルの名前も読み取ります。この変数は後でデプロイ時に定義します。
4.3. 機能1の実装
最初の関数では、API Gatewayからペイロードを取得する方法(PUTリクエストやPOSTリクエストから取得する方法など)を説明します。
public void handleRequest(
InputStream inputStream,
OutputStream outputStream,
Context context)
throws IOException {
JSONParser parser = new JSONParser();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
JSONObject responseJson = new JSONObject();
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.defaultClient();
DynamoDB dynamoDb = new DynamoDB(client);
try {
JSONObject event = (JSONObject) parser.parse(reader);
if (event.get("body") != null) {
Person person = new Person((String) event.get("body"));
dynamoDb.getTable(DYNAMODB__TABLE__NAME)
.putItem(new PutItemSpec().withItem(new Item().withNumber("id", person.getId())
.withString("name", person.getName())));
}
JSONObject responseBody = new JSONObject();
responseBody.put("message", "New item created");
JSONObject headerJson = new JSONObject();
headerJson.put("x-custom-header", "my custom header value");
responseJson.put("statusCode", 200);
responseJson.put("headers", headerJson);
responseJson.put("body", responseBody.toString());
} catch (ParseException pex) {
responseJson.put("statusCode", 400);
responseJson.put("exception", pex);
}
OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8");
writer.write(responseJson.toString());
writer.close();
}
-
前述のように、Lambdaプロキシ統合を使用するようにAPIを後で設定します。 API Gatewayは、
InputStream
パラメータでLambda関数に完全なリクエストを渡す必要があります。
含まれているJSON構造から関連する属性を選択するだけです。
ご覧のとおり、このメソッドは基本的に3つのステップで構成されています。
-
入力ストリームから
body
オブジェクトを取得して、
その中の
Person
オブジェクト
。その
Person
オブジェクトをDynamoDBテーブルに格納する
-
JSONオブジェクトを作成します. これは、次のように複数の属性を保持できます.
レスポンスの
body
、カスタムヘッダ、HTTPステータスコード
ここで言及する価値がある1つのポイント:API Gatewayは
body
が
String
であることを要求します(要求と応答の両方のために)。
API Gatewayから
String
を
body
として取得することを期待しているので、
body
を
String
にキャストし、
Person
オブジェクトを初期化します。
Person person = new Person((String) event.get("body"));
API Gatewayはまた、レスポンス
body
が
String
であることを期待しています。
responseJson.put("body", responseBody.toString());
このトピックは公式文書には明示的に記載されていません。
しかし、よく見てみると、body属性が両方のスニペットhttps://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrationsで
String
であることがわかります。
リクエスト用
html#api-gateway-simple-lamda入力プロキシ[応答用]-integrations.html#api-gateway-simple-proxy-lambda-output-format
JSONがAPI GatewayとLambda関数の間の形式であっても、利点は明らかです。実際の本文には、プレーンテキスト、JSON、XMLなどを含めることができます。フォーマットを正しく処理するのはLambda関数の役割です。
AWSコンソールで機能をテストするときに、リクエストとレスポンスの本体がどのように見えるかがわかります。
以下の2つの機能についても同様です。
4.4.
機能2の
実装
2番目のステップでは、IDを使用してデータベースから
Person
アイテムを取得するためのパスパラメータまたはクエリ文字列パラメータの使用方法を説明します。
public void handleGetByParam(
InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
JSONParser parser = new JSONParser();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
JSONObject responseJson = new JSONObject();
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.defaultClient();
DynamoDB dynamoDb = new DynamoDB(client);
Item result = null;
try {
JSONObject event = (JSONObject) parser.parse(reader);
JSONObject responseBody = new JSONObject();
if (event.get("pathParameters") != null) {
JSONObject pps = (JSONObject) event.get("pathParameters");
if (pps.get("id") != null) {
int id = Integer.parseInt((String) pps.get("id"));
result = dynamoDb.getTable(DYNAMODB__TABLE__NAME).getItem("id", id);
}
}
else if (event.get("pathParameters") != null) {
JSONObject pps = (JSONObject) event.get("pathParameters");
if (pps.get("id") != null) {
int id = Integer.parseInt((String) pps.get("id"));
result = dynamoDb.getTable(DYNAMODB__TABLE__NAME).getItem("id", id);
}
}
if (result != null) {
Person person = new Person(result.toJSON());
responseBody.put("Person", person);
responseJson.put("statusCode", 200);
} else {
responseBody.put("message", "No item found");
responseJson.put("statusCode", 404);
}
JSONObject headerJson = new JSONObject();
headerJson.put("x-custom-header", "my custom header value");
responseJson.put("headers", headerJson);
responseJson.put("body", responseBody.toString());
} catch (ParseException pex) {
responseJson.put("statusCode", 400);
responseJson.put("exception", pex);
}
OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8");
writer.write(responseJson.toString());
writer.close();
}
繰り返しますが、3つのステップが関連しています。
-
pathParameters
か
queryStringParameters
かを確認します.
id
属性を持つ配列が存在します。
-
true
の場合、所属値を使用して
Person
アイテムを要求します.
データベースからのそのID。
-
受け取ったアイテムのJSON表現をレスポンスに追加します.
公式ドキュメントはhttps://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambdaのより詳細な説明を提供します-input-format[input format]およびhttps://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambdaプロキシ統合用の-output-format[出力フォーマット]。
4.5. 建築基準コード
繰り返しますが、Mavenを使用してコードを簡単に構築できます。
mvn clean package shade:shade
JARファイルは
target
フォルダーの下に作成されます。
4.6. DynamoDBテーブルを作成する
リンク:/aws-lambda-dynamodb-java#dynamodb-table[AWS Lambda Using DynamoDB with Java]で説明されているようにテーブルを作成できます。
テーブル名として
Person
、主キーの名前として
id
、主キーの種類として
Number
を選択しましょう。
4.7. AWSコンソールを介したコードのデプロイ
コードを作成してテーブルを作成したら、関数を作成してコードをアップロードできます。
これは、次のリンクから手順1〜5を繰り返すことで実行できます。
以下の関数名を使用しましょう:
-
handleRequest
メソッドの
StorePersonFunction
(機能1)
handleGetByParam
メソッドの**
_GetPersonByHTTPParamFunction
_
(機能2)
また、環境変数
TABLE
NAME
を値
“ Person” __で定義する必要があります。
4.8. 機能をテストする
実際のAPI Gatewayの部分に進む前に、Lambda関数が正しく実行されていてProxy Integration形式を処理できることを確認するためだけに、AWSコンソールでクイックテストを実行できます。
AWSコンソールからLambda関数をテストすることは、リンク:/java-aws-lambda#invoke[AWS Lambda with Java]の記事に記載されているように機能します。
ただし、
テストイベントを作成するときは、私たちの関数が期待している特別なProxy Integration形式
を考慮する必要があります。
API Gateway AWS Proxy
テンプレートを使用してそれをニーズに合わせてカスタマイズするか、次のイベントをコピーして貼り付けることができます。
StorePersonFunction
では、これを使用する必要があります。
{
"body": "{\"id\": 1, \"name\": \"John Doe\"}"
}
前述したように、
body
は、JSON構造を含む場合でも、
String
型でなければなりません。その理由は、API Gatewayがリクエストを同じ形式で送信するためです。
以下の応答が返されるはずです。
{
"isBase64Encoded": false,
"headers": {
"x-custom-header": "my custom header value"
},
"body": "{\"message\":\"New item created\"}",
"statusCode": 200
}
ここでは、レスポンスの
body
が
String
であることがわかりますが、これにはJSON構造が含まれています。
__GetPersonByHTTPParamFunction.の入力を見てみましょう。
パスパラメータ機能をテストするための入力は、次のようになります。
{
"pathParameters": {
"id": "1"
}
}
クエリ文字列パラメータを送信するための入力は次のようになります。
{
"queryStringParameters": {
"id": "1"
}
}
応答として、どちらの場合でも以下のようにします
{
"headers": {
"x-custom-header": "my custom header value"
},
"body": "{\"Person\":{\n \"id\": 88,\n \"name\": \"John Doe\"\n}}",
"statusCode": 200
}
繰り返しますが、
body
は
String
です。
5 APIの作成とテスト
前のセクションでLambda関数を作成してデプロイした後、これでAWSコンソールを使用して実際のAPIを作成できます** 。
基本的なワークフローを見てみましょう。
-
AWSアカウントでAPIを作成してください.
-
APIのリソース階層にリソースを追加します.
-
リソースに対して1つ以上のメソッドを作成します.
-
メソッドと所属するLambdaの間の統合を設定する
関数。
次のセクションでは、2つの機能それぞれについてステップ2〜4を繰り返します。
5.1. APIを作成する
APIを作成するには、次のことが必要です。
-
API Gatewayコンソールにサインインします
https://console.aws.amazon.com/apigatewayで
。[はじめに]をクリックし、[新しいAPI]を選択します。
-
APIの名前(
TestAPI
)を入力し、をクリックして承認します
「APIを作成する」
APIを作成したので、これでAPI構造を作成してそれをLambda関数にリンクさせることができます。
5.2. 機能1のAPI構造
StorePersonFunction
には次の手順が必要です。
-
[Resources]ツリーの下の親リソース項目を選択してから、
[Actions]ドロップダウンメニューから[Create Resource]を選択します。次に、[New Child Resource]ペインで次の操作を行います。
-
「Resource Name」入力テキストフィールドに、名前として「Persons」と入力します。
-
「リソースパス」入力テキストフィールドはデフォルト値のままにします。
-
「Create Resource」を選択してください
-
作成したばかりのリソースを選択し、「Create Method」を選択します.
-
[Actions]ドロップダウンメニューをクリックして、次の手順を実行します。
-
HTTP methodドロップダウンリストからPUTを選択してから、
選択を保存するためのチェックマークアイコン
** 統合タイプは「Lambda Function」のままにして、
「Lambda Proxy統合を使用する」オプション
** ラムダを展開した「ラムダ地域」から地域を選択してください
以前の機能
** 「Lambda関数」に
「StorePersonFunction」
と入力します。
-
「保存」を選択し、「追加」のメッセージが表示されたら「OK」で確認します.
ラムダ関数の許可」
5.3. 機能2のAPI構造 – パスパラメータ
パスパラメータを取得する手順は似ています。
-
“ Resources”ツリーの下の
/person
リソースアイテムを選択
次に、[Actions]ドロップダウンメニューから[Create Resource]を選択します。次に、[New Child Resource]ペインで次の操作を行います。
-
“Resource Name”入力テキストフィールドに、名前として
“Person”
を入力します。 -
“Resource Path”入力テキストフィールドを
“\ {id}”
に変更します。 -
「Create Resource」を選択してください
-
作成したばかりのリソースを選択し、「Create Method」を選択します.
-
[Actions]ドロップダウンメニューをクリックして、次の手順を実行します。
-
HTTP methodドロップダウンリストからGETを選択してから、
選択を保存するためのチェックマークアイコン
** 統合タイプは「Lambda Function」のままにして、
「Lambda Proxy統合を使用する」オプション
** ラムダを展開した「ラムダ地域」から地域を選択してください
以前の機能
** 「Lambda関数」に
「GetPersonByHTTPParamFunction」
と入力します。
-
「保存」を選択し、「追加」のメッセージが表示されたら「OK」で確認します.
ラムダ関数の許可」
注:
__GetPersonByPathParamFunction
はこのパラメーターが正確にこのように命名されることを期待しているため、ここでは “Resource Path”パラメーターを
“\ {id}” __に設定することが重要です。
5.4. 機能2のAPI構造 – クエリ文字列パラメータ
クエリ文字列パラメータを受け取る手順は少し異なります。リソースを作成する必要はありませんが、
id
パラメータのクエリパラメータを作成する必要があるからです。
-
“ Resources”ツリーの下の
/person
リソース項目を選択してください.
[Actions]ドロップダウンメニューから[Create Method]を選択し、次の手順を実行します。
-
[HTTP method]ドロップダウンリストから[GET]を選択し、次に
選択を保存するためのチェックマークアイコン
** 統合タイプは「Lambda Function」のままにして、
「Lambda Proxy統合を使用する」オプション
** ラムダを展開した「ラムダ地域」から地域を選択してください
以前の機能
** “Lambda Function”に
“GetPersonByHTTPParamFunction”
と入力します。
-
「保存」を選択し、「追加」のメッセージが表示されたら「OK」で確認します.
ラムダ関数の許可」
。右側の「Method Request」を選択して、以下を実行します。
ステップ:
-
URL Query String Parametersリストを展開
-
“ Add Query String”をクリックしてください。
-
名前フィールドに
“ id”
と入力し、チェックマークアイコンを選択して保存します。 -
「必須」チェックボックスを選択します
-
上部の「Request validator」の横にあるペンの記号をクリックします。
パネルで、「クエリ文字列パラメータとヘッダを検証する」を選択し、チェックマークアイコンを選択します。
注:
__GetPersonByHTTPParamFunction
はこのパラメーターの名前が正確に同じであると想定しているため、 “Query String”パラメーターを
“id” __に設定することが重要です。
5.5. APIのテスト
これでAPIは完成しましたが、まだ公開されていません。
公開する前に、まずコンソールからクイックテストを実行したいと思います
。
そのためには、「Resources」ツリーでテストするそれぞれの方法を選択して「Test」ボタンをクリックします。次の画面では、HTTPでクライアントに送信するので、入力内容を入力します。
StorePersonFunction
の場合、「Request Body」フィールドに次の構造を入力する必要があります。
{
"id": 2,
"name": "Jane Doe"
}
パスパラメータを含む
GetPersonByHTTPParamFunction
の場合、「Path」の下の「\ {id}」フィールドに値として
2
を入力する必要があります。
__GetPersonByHTTPParamFunction
withクエリ文字列パラメータの場合、「クエリ文字列」の下の「\ {people}」フィールドに値として
id = 2__を入力する必要があります。
5.6. APIのデプロイ
これまで、私たちのAPIは一般に公開されていなかったため、AWSコンソールからしか利用できませんでした。
前述したように、
APIをデプロイするときには、それをステージと関連付ける必要があります。これは、APIの時点におけるスナップショットのようなものです。 APIを再デプロイする場合は、既存のステージを更新するか、新しいステージを作成することができます
。
APIのURLスキームがどのようになるかを見てみましょう。
https://{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}
展開には次の手順が必要です。
-
「API」ナビゲーションペインで特定のAPIを選択してください
-
[Resources]ナビゲーションペインで[Actions]を選択し、[Deploy]を選択します.
[アクション]ドロップダウンメニューの[API]。 「Deployment stage」ドロップダウンから「[New Stage]」を選択し、
「ステージ名」の「test」、およびオプションで説明を入力します。
ステージと展開
。 「配置」を選択して配置を開始します。
最後のステップの後で、コンソールはAPIのルートURLを提供します、例えば
https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test
。
5.7. エンドポイントの呼び出し
APIは現在公開されているので、
必要なHTTPクライアントを使用して呼び出すことができます
。
cURL
の場合、呼び出しは次のようになります。
StorePersonFunction
:
curl -X PUT 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons' \
-H 'content-type: application/json' \
-d '{"id": 3, "name": "Richard Roe"}'
パスパラメータの
_GetPersonByHTTPParamFunction
_
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/3' \
-H 'content-type: application/json'
クエリ文字列パラメータの場合は
GetPersonByHTTPParamFunction
:
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons?id=3' \
-H 'content-type: application/json'
6. 結論
この記事では、AWS API Gatewayを使用して、AWS Lambda関数をRESTエンドポイントとして使用可能にする方法について説明しました。
API Gatewayの基本概念と用語を調べ、Lambda Proxy Integrationを使用してLambda関数を統合する方法を学びました。
最後に、APIの作成、デプロイ、およびテスト方法を見ました。
いつものように、この記事のすべてのコードはhttps://github.com/eugenp/tutorials/tree/master/aws-lambda[on GitHub]から入手できます。