知識庫 / DevOps RSS 訂閱

使用 AWS Lambda 與 API Gateway

Cloud,DevOps,REST
HongKong
4
03:52 AM · Dec 06 ,2025

1. 概述

AWS Lambda 是亞馬遜雲服務(Amazon Web Services)提供的無服務器計算服務。

在之前的兩篇文章中,我們討論瞭如何使用 Java 創建 AWS Lambda 函數,以及如何從 Lambda 函數訪問 DynamoDB。

在本教程中,我們將討論 如何將 Lambda 函數發佈為 REST 端點,使用 AWS Gateway

我們將深入探討以下主題:

  • API Gateway 的基本概念和術語
  • 使用 Lambda 代理集成(Lambda Proxy integration)將 Lambda 函數與 API Gateway 集成
  • 創建 API、其結構以及如何將 API 資源映射到 Lambda 函數
  • API 的部署和測試

2. 基本概念與術語

API 網關是一個完全託管服務,它使開發者能夠創建、發佈、維護、監控和保護任何規模的 API。

我們可以實現一致且可擴展的基於 HTTP 的編程接口(也稱為 RESTful 服務)來訪問後端服務,例如 Lambda 函數,以及其他 AWS 服務(例如 EC2、S3、DynamoDB)和任何 HTTP 端點。

功能包括但不限於:

  • 流量管理
  • 授權和訪問控制
  • 監控
  • API 版本管理
  • 限流請求以防止攻擊

與 AWS Lambda 類似,API 網關會自動擴展並按 API 調用計費。

詳細信息請參閲 官方文檔

2.1. 術語

API 網關 是一個 AWS 服務,用於創建、部署和管理 RESTful 應用程序編程接口,以公開後端 HTTP 端點、AWS Lambda 函數和其他 AWS 服務。

API 網關 API 是一個包含資源和方法的集合,可以與 Lambda 函數、其他 AWS 服務或後端 HTTP 端點集成。該 API 由形成 API 結構資源的 API 資源組成。 每個 API 資源都可以公開一個或多個 API 方法,這些方法必須具有唯一的 HTTP 動詞。

要發佈 API,我們需要創建API 部署並將其與所謂的階段關聯。 階段就像 API 在特定時間點的快照。 如果重新部署 API,我們可以更新現有階段或創建新階段。 這樣,就可以在同一時間同時訪問 API 的不同版本,例如dev 階段、test 階段,甚至多個生產版本,如v1v2 等。

Lambda 代理集成 是 Lambda 函數與 API 網關之間集成的簡易配置。

API 網關將整個請求作為輸入發送到後端 Lambda 函數。 響應方面,API 網關將 Lambda 函數的輸出轉換為前端 HTTP 響應。

3. 依賴項

我們需要與在《使用 DynamoDB 進行 AWS Lambda(Java)》文章中相同的依賴項。

此外,我們還需要 JSON Simple 庫:

<dependency>
    <groupId>com.googlecode.json-simple</groupId>
    <artifactId>json-simple</artifactId>
    <version>1.1.1</version>
</dependency>

4. 開發和部署 Lambda 函數

在本節中,我們將使用 Java 開發和構建我們的 Lambda 函數,並通過 AWS 控制枱進行部署,並運行一個快速測試。

為了演示 API Gateway 與 Lambda 集成的基本功能,我們將創建兩個函數:

  • 函數 1: 接收來自 API 的負載,使用 PUT 方法
  • 函數 2: 演示如何使用來自 API 的 HTTP 路徑參數或 HTTP 查詢參數

在實現方面,我們將創建一個 RequestHandler 類,該類包含兩個方法——一個用於每個函數。

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 類,它具有兩個屬性。 值得注意的是,Person(String) 構造函數,它接受一個 JSON 字符串。

4.2. RequestHandler 類實現

正如在“使用 Java 構建 AWS Lambda”文章中所述,我們將創建一個 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()。 無論如何,我們可以在同一類中定義進一步的函數,正如我們在這裏所做的那樣。 另一種選擇是在每個函數中創建RequestStreamHander的一個實現。

在我們的特定情況下,我們選擇了前者以簡化操作。 但是,必須根據性能和代碼可維護性等因素進行案例分析。

我們還從TABLE_NAME環境變量中讀取我們的 DynamoDB 表名。 我們將在部署過程中稍後定義該變量。

4.3. 函數 1 的實現

在我們的第一個函數中,我們希望演示如何從 API 網關獲取負載(例如來自 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();
}

如前所述,我們將配置 API 以使用 Lambda 代理集成。我們預計 API Gateway 將完整的請求通過 InputStream 參數傳遞到 Lambda 函數。

我們只需從包含的 JSON 結構中選擇相關的屬性。

正如您所看到的,該方法基本上由三個步驟組成:

  1. 從我們的輸入流中獲取 body 對象並從該對象創建 Person 對象
  2. 將該 Person 對象存儲在 DynamoDB 表中
  3. 構建一個 JSON 對象,它可以包含多個屬性,例如響應的 body、自定義標頭以及 HTTP 狀態碼

值得一提的一點是:API Gateway 期望 body 是一個 String (對於請求和響應)。

由於我們預計從 API Gateway 獲得 body 作為 String,因此我們將其 body 轉換為 String 並初始化我們的 Person 對象:

Person person = new Person((String) event.get("body"));

API 網關還期望響應 body 是一個 String

responseJson.put("body", responseBody.toString());

本主題並未在官方文檔中明確提及。但是,如果我們仔細觀察,可以發現請求體中,`body` 屬性在兩個片段中都是一個 String用於請求,以及 用於響應

優勢應該顯而易見:即使 JSON 是 API Gateway 和 Lambda 函數之間使用的格式,實際的請求體可以包含純文本、JSON、XML 或其他任何內容。然後,Lambda 函數有責任正確處理這些格式。

稍後,當我們通過 AWS 控制枱測試我們的函數時,我們會看到請求和響應體的具體情況。

同樣,這也適用於以下兩個函數。

4.4. 函數 2 的實現

在下一步中,我們將演示如何使用路徑參數或查詢字符串參數從數據庫中檢索一個 Person 項目,其 ID 如下:

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("queryStringParameters") != null) {
            JSONObject qps = (JSONObject) event.get("queryStringParameters");
            if (qps.get("id") != null) {

                int id = Integer.parseInt((String) qps.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();
}

再次強調,有三個關鍵步驟:

  1. 我們檢查是否存在包含 pathParametersqueryStringParameters 數組以及具有 id 屬性的項。
  2. 如果值為 true,則使用該值從數據庫中請求具有該 ID 的 Person 記錄。
  3. 我們將接收到的項的 JSON 表示形式添加到響應中。

官方文檔提供了關於 輸入格式輸出格式以及代理集成信息的更詳細説明。

4.5. 構建代碼

再次,我們可以使用 Maven 輕鬆構建我們的代碼:

mvn clean package shade:shade

JAR 文件將被創建在 target 文件夾下。

4.6. 創建 DynamoDB 表

我們可以按照《使用 Java 在 AWS Lambda 中使用 DynamoDB》中所述的方式創建表。

讓我們選擇 Person 作為表名, id 作為主鍵名,以及 Number 作為主鍵類型。

4.7. 通過 AWS 控制枱部署代碼

在構建我們的代碼並創建表之後,我們可以現在創建函數並上傳代碼。

這可以通過重複 AWS Lambda with Java 文章中的步驟 1-5,針對我們的兩個方法各執行一次來實現。

讓我們使用以下函數名稱:

  • StorePersonFunction 用於 handleRequest 方法 (函數 1)
  • GetPersonByHTTPParamFunction 用於 handleGetByParam 方法 (函數 2)

我們還需要定義環境變量 TABLE_NAME 的值為 “Person”

4.8. 測試函數

在繼續進行實際的 API Gateway 部分之前,我們可以通過在 AWS 控制枱中運行一個快速測試,僅用於檢查我們的 Lambda 函數是否正確運行並能處理 Proxy 集成格式。

從 AWS 控制枱測試 Lambda 函數的工作方式與 AWS Lambda with Java 文章中所描述的相同。

然而,在創建測試事件時,我們需要考慮 Proxy 集成格式的特殊性,而我們的函數正在等待。我們可以使用 API Gateway AWS Proxy 模板並根據需要進行自定義,或者複製並粘貼以下事件:

對於 StorePersonFunction,我們應該使用以下內容:

{
    "body": "{\"id\": 1, \"name\": \"John Doe\"}"
}

如前所述,body 必須為 String 類型,即使包含 JSON 結構。原因在於 API 網關將發送請求時採用相同的格式。

應返回以下響應:

{
    "isBase64Encoded": false,
    "headers": {
        "x-custom-header": "my custom header value"
    },
    "body": "{\"message\":\"New item created\"}",
    "statusCode": 200
}

在這裏,我們可以看到我們的響應主體是一個 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. 為資源創建一種或多種方法。
  4. 設置方法與相應的 Lambda 函數之間的集成。

在後續部分中,我們將為我們兩個函數中的每一種重複步驟 2-4。

5.1. 創建 API

為了創建 API,我們需要執行以下操作:

  1. 登錄到 API 網關控制枱:https://console.aws.amazon.com/apigateway
  2. 點擊“開始使用”並選擇“新 API”
  3. 輸入 API 的名稱(TestAPI)並點擊“創建 API”

創建 API 後,我們可以創建 API 結構並將其鏈接到我們的 Lambda 函數。

5.2. 函數 1 的 API 結構

以下步驟適用於我們的 <em >StorePersonFunction</em>

  1. 選擇“資源”樹下父級資源項,然後從“操作”下拉菜單中選擇“創建資源”。 接下來,需要在“新子資源”面板中執行以下操作:
    • 在“資源名稱”輸入框中輸入“Persons”
    • 在“資源路徑”輸入框中保留默認值
    • 選擇“創建資源”
  2. 選擇剛剛創建的資源,從“操作”下拉菜單中選擇“創建方法”,並執行以下步驟:
    • 從 HTTP 方法下拉列表中選擇 PUT,然後選擇勾選圖標以保存選擇
    • 將“Lambda 函數”集成類型留為默認,並選擇“使用 Lambda 代理集成”選項
    • 從“Lambda 區域”中選擇我們部署的 Lambda 函數之前的區域
    • 在“Lambda 函數”中輸入 `StorePersonFunction`
  3. 選擇“保存”並確認提示“為 Lambda 函數添加權限”時點擊“OK”

5.3. API 結構 – 路徑參數 (Function 2)

以下是檢索路徑參數的步驟,與之前類似:

  1. 選擇“Resources”樹下的人員資源項,然後從“Actions”下拉菜單中選擇“Create Resource”。 接下來,在“New Child Resource”面板中執行以下操作:
    • 在“Resource Name”輸入文本字段中輸入“Person”
    • 將“Resource Path”輸入文本字段更改為“{id}”
    • 選擇“Create Resource”
  2. 選擇剛剛創建的資源,從“Actions”下拉菜單中選擇“Create Method”,並執行以下步驟:
    • 從HTTP方法下拉列表中選擇GET,然後選擇勾選圖標以保存選擇
    • 將“Lambda Function”集成類型保持不變,並選擇“Use Lambda Proxy Integration”選項
    • 從“Lambda Region”中選擇我們部署的Lambda函數所在的區域
    • 在“Lambda Function”中輸入“GetPersonByHTTPParamFunction”
  3. 選擇“Save”並確認“Add Permission to Lambda Function”提示

注意:在這裏,將“Resource Path”參數設置為“{id}”,因為我們的“GetPersonByPathParamFunction”期望該參數命名方式與此完全一致。

5.4. API 結構 – 函數 2 – 查詢字符串參數

接收查詢字符串參數的步驟有所不同,因為我們不需要創建資源,而是需要為 id 參數創建一個查詢參數:

  1. 選擇“資源”樹下的 /persons 資源項,從“操作”下拉菜單中選擇“創建方法”,並執行以下步驟:
    • 選擇 HTTP 方法下拉菜單中的 GET,然後選擇勾選圖標以保存選擇
    • 將“Lambda 函數”集成類型留為默認,並選擇“使用 Lambda 代理集成”選項
    • 從“Lambda 區域”中選擇我們部署 Lambda 函數之前使用的區域
    • 在“Lambda 函數”中輸入 “GetPersonByHTTPParamFunction”
  2. 選擇“保存”並確認提示“添加權限到 Lambda 函數”
  3. 選擇右側的“方法請求”,並執行以下步驟:
    • 展開“URL 查詢字符串參數”列表
    • 點擊“添加查詢字符串”
    • 在“名稱”字段中輸入 “id”,並選擇勾選圖標以保存
    • 選擇“必需”複選框
    • 點擊面板頂部“請求驗證器”旁邊的筆符號,選擇“驗證查詢字符串參數和標頭”,並選擇勾選圖標

注意:將“查詢字符串”參數設置為 “id”,因為我們的 GetPersonByHTTPParamFunction 期望該參數名稱與此完全匹配。

5.5. API 測試

我們的 API 已經準備就緒,但尚未公開。在發佈之前,我們希望首先從控制枱運行一個快速測試。

為此,我們可以選擇要測試的相應方法,在“資源”樹中進行選擇,然後點擊“測試”按鈕。在接下來的屏幕上,我們可以輸入我們的輸入,就像通過 HTTP 與客户端發送一樣。

對於 StorePersonFunction,我們需要將以下結構輸入到“請求體”字段中:

{
    "id": 2,
    "name": "Jane Doe"
}

對於使用 GetPersonByHTTPParamFunction 的路徑參數,需要將 2 作為值輸入到“{id}”字段下方的“Path”中。

對於使用 GetPersonByHTTPParamFunction 的查詢字符串參數,需要將 id=2 作為值輸入到“{persons}”字段下方的“Query Strings”中。

5.6. API部署

此前,我們的API未公開,僅可通過AWS控制枱訪問。

如前所述,在部署API時,我們需要將其與一個階段關聯起來,該階段就像API在特定時間點的快照。如果重新部署API,我們可以更新現有階段或創建一個新的階段。

讓我們看看我們的API的URL方案將是什麼樣的:

https://{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}

以下步驟用於部署:

  1. 選擇“API”導航窗格中的特定API。
  2. 在“資源”導航窗格中選擇“操作”,然後從“操作”下拉菜單中選擇“部署API”。
  3. 從“部署階段”下拉菜單中選擇“[新階段]”,在“階段名稱”中輸入“test”,並可選地提供階段和部署的描述。
  4. 通過選擇“部署”來觸發部署。
部署完成後,控制枱將提供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 Lambda 函數作為 REST 端點使用,藉助 AWS API Gateway。

我們研究了 API Gateway 的基本概念和術語,並學習瞭如何使用 Lambda 代理集成進行 Lambda 函數集成。

最後,我們看到了如何創建、部署和測試 API。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.