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つのステップで構成されています。

  1. 入力ストリームから body オブジェクトを取得して、

その中の Person オブジェクト 。その Person オブジェクトをDynamoDBテーブルに格納する

  1. 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つのステップが関連しています。

  1. pathParameters queryStringParameters かを確認します.

id 属性を持つ配列が存在します。

  1. true の場合、所属値を使用して Person アイテムを要求します.

データベースからのそのID。

  1. 受け取ったアイテムの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を作成できます** 。

基本的なワークフローを見てみましょう。

  1. AWSアカウントでAPIを作成してください.

  2. APIのリソース階層にリソースを追加します.

  3. リソースに対して1つ以上のメソッドを作成します.

  4. メソッドと所属するLambdaの間の統合を設定する

関数。

次のセクションでは、2つの機能それぞれについてステップ2〜4を繰り返します。

5.1. APIを作成する

APIを作成するには、次のことが必要です。

  1. API Gatewayコンソールにサインインします

https://console.aws.amazon.com/apigatewayで 。[はじめに]をクリックし、[新しいAPI]を選択します。

  1. APIの名前( TestAPI )を入力し、をクリックして承認します

「APIを作成する」

APIを作成したので、これでAPI構造を作成してそれをLambda関数にリンクさせることができます。

5.2. 機能1のAPI構造

StorePersonFunction には次の手順が必要です。

  1. [Resources]ツリーの下の親リソース項目を選択してから、

[Actions]ドロップダウンメニューから[Create Resource]を選択します。次に、[New Child Resource]ペインで次の操作を行います。

  • 「Resource Name」入力テキストフィールドに、名前として「Persons」と入力します。

  • 「リソースパス」入力テキストフィールドはデフォルト値のままにします。

  • 「Create Resource」を選択してください

    1. 作成したばかりのリソースを選択し、「Create Method」を選択します.

[Actions]ドロップダウンメニューをクリックして、次の手順を実行します。

  • HTTP methodドロップダウンリストからPUTを選択してから、

選択を保存するためのチェックマークアイコン ** 統合タイプは「Lambda Function」のままにして、

「Lambda Proxy統合を使用する」オプション ** ラムダを展開した「ラムダ地域」から地域を選択してください

以前の機能 ** 「Lambda関数」に 「StorePersonFunction」 と入力します。

  1. 「保存」を選択し、「追加」のメッセージが表示されたら「OK」で確認します.

ラムダ関数の許可」

5.3. 機能2のAPI構造 - パスパラメータ

パスパラメータを取得する手順は似ています。

  1. “ Resources”ツリーの下の /person リソースアイテムを選択

次に、[Actions]ドロップダウンメニューから[Create Resource]を選択します。次に、[New Child Resource]ペインで次の操作を行います。

  • "Resource Name"入力テキストフィールドに、名前として "Person" を入力します。

  • "Resource Path"入力テキストフィールドを "\ {id}" に変更します。

  • 「Create Resource」を選択してください

    1. 作成したばかりのリソースを選択し、「Create Method」を選択します.

[Actions]ドロップダウンメニューをクリックして、次の手順を実行します。

  • HTTP methodドロップダウンリストからGETを選択してから、

選択を保存するためのチェックマークアイコン ** 統合タイプは「Lambda Function」のままにして、

「Lambda Proxy統合を使用する」オプション ** ラムダを展開した「ラムダ地域」から地域を選択してください

以前の機能 ** 「Lambda関数」に 「GetPersonByHTTPParamFunction」 と入力します。

  1. 「保存」を選択し、「追加」のメッセージが表示されたら「OK」で確認します.

ラムダ関数の許可」

注: __GetPersonByPathParamFunction はこのパラメーターが正確にこのように命名されることを期待しているため、ここでは "Resource Path"パラメーターを "\ {id}" __に設定することが重要です。

5.4. 機能2のAPI構造 - クエリ文字列パラメータ

クエリ文字列パラメータを受け取る手順は少し異なります。リソースを作成する必要はありませんが、 id パラメータのクエリパラメータを作成する必要があるからです。

  1. “ Resources”ツリーの下の /person リソース項目を選択してください.

[Actions]ドロップダウンメニューから[Create Method]を選択し、次の手順を実行します。

  • [HTTP method]ドロップダウンリストから[GET]を選択し、次に

選択を保存するためのチェックマークアイコン ** 統合タイプは「Lambda Function」のままにして、

「Lambda Proxy統合を使用する」オプション ** ラムダを展開した「ラムダ地域」から地域を選択してください

以前の機能 ** "Lambda Function"に "GetPersonByHTTPParamFunction" と入力します。

  1. 「保存」を選択し、「追加」のメッセージが表示されたら「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}

展開には次の手順が必要です。

  1. 「API」ナビゲーションペインで特定のAPIを選択してください

  2. [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]から入手できます。