在無伺服器架構中,API Gateway 與 Lambda 的整合是構建 REST API 的關鍵。本文將探討如何利用 Lambda 代理整合簡化開發流程,並確保 API 的穩定性和安全性。首先,我們會探討如何建立請求驗證器和模型,以確保 API 的輸入資料符合預期格式,接著會示範如何使用 CloudFormation 範本來簡化 API 的佈署和管理。同時,文章也會提供 Lambda 函式的程式碼範例,詳細說明如何處理 API Gateway 傳遞的請求,並傳回正確的回應。最後,我們將會探討 Lambda 代理整合的理論基礎,以及在實際應用中需要注意的事項,例如輸入和輸出格式的規範,以幫助讀者更深入地理解這個重要的無伺服器開發模式。

使用 API Gateway 建置無伺服器 REST API:第 2 章

建立請求驗證器與模型

在建置無伺服器 REST API 的過程中,我們需要確保傳入的請求符合預期的格式。這可以透過建立請求模型和驗證器來實作。首先,我們使用 AWS CLI 指令 create-request-validator 來建立一個請求驗證器,以驗證傳入的 JSON 請求內容。

aws apigateway create-request-validator \
--rest-api-id dqnqdyb3z2 \
--name greetingRequestValidator \
--validate-request-body \
--profile admin

內容解密:

  • create-request-validator 指令用於建立請求驗證器。
  • --rest-api-id 指定了要與之關聯的 REST API ID。
  • --name 為驗證器命名。
  • --validate-request-body 表示驗證器將檢查請求主體。

接下來,我們使用 put-method 指令來設定 POST 方法,並指定請求模型和驗證器。

aws apigateway put-method \
--rest-api-id dqnqdyb3z2 \
--resource-id ffknxp \
--http-method POST \
--authorization-type "NONE" \
--request-models application/json=greetingRequestModel \
--request-validator-id 549e4h \
--region us-east-1 \
--profile admin

內容解密:

  • put-method 指令用於設定指定資源的 HTTP 方法。
  • --request-models 指定了請求的內容型別與對應的模型。
  • --request-validator-id 指定了要使用的請求驗證器。

使用 CloudFormation 範本建立 API

我們也可以使用 CloudFormation 範本來建立 API。首先,定義範本版本、描述、RestApi 和路徑部分。

MyRequestValidationModel:
  Type: AWS::ApiGateway::Model
  Properties:
    ContentType: application/json
    Description: Greeting Request Model
    Name: GreetingRequestModel
    RestApiId: !Ref MyRestAPI
    Schema: '{"$schema": "http://json-schema.org/draft-04/schema#",
              "title": "greetingModel",
              "type": "object",
              "properties": {
                "user" : {"type": "object",
                          "properties": {
                            "name" : {"type" : "string"}
                          }
                },
                "greeting" : {"type": "object",
                              "properties" : {
                                "time" : {"type" : "string"}
                              }
                }
              },
              "required" : ["user", "greeting"]
            }'

內容解密:

  • AWS::ApiGateway::Model 用於定義請求模型。
  • Schema 定義了請求的 JSON 結構,包括必要的欄位。

然後,建立請求驗證器並將其與方法關聯。

MyRequestValidator:
  Type: AWS::ApiGateway::RequestValidator
  Properties:
    Name: GreetingRequestValidator
    RestApiId: !Ref MyRestAPI
    ValidateRequestBody: true
    ValidateRequestParameters: false

MyMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    AuthorizationType: NONE
    HttpMethod: POST
    Integration:
      Type: AWS
      IntegrationHttpMethod: POST
      PassthroughBehavior: WHEN_NO_TEMPLATES
      RequestTemplates:
        application/json: "{ \"name\" : $input.json('$.user.name'), \"time\": $input.json('$.greeting.time') }"
      Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:${AWS::AccountId}:function:${LAMBDA_NAME}/invocations'
        - LAMBDA_NAME: !ImportValue LambdaForApiGateway
      RequestModels:
        application/json: !Ref MyRequestValidationModel
      RequestValidatorId: !Ref MyRequestValidator

內容解密:

  • AWS::ApiGateway::RequestValidator 定義了請求驗證器。
  • MyMethod 定義了 API 方法,並將請求模型和驗證器與其關聯。

測試 API

可以使用 Postman 或其他 REST 客戶端測試 API。如果傳送正確格式的請求,應獲得預期的回應。否則,將傳回錯誤訊息。

Lambda 與 API 的代理整合

在本文中,我們將組態 API 以作為 Lambda 的代理。首先,需要建立一個新的 Lambda 函式,該函式實作 RequestStreamHandler 以處理傳入的請求。

使用 API Gateway 建立無伺服器 REST API - 第 2 章:代理整合實作

建立 Lambda 函式進行代理整合

本章節將使用 AWS 提供的 Lambda 代理整合範例,建立一個簡單的 Lambda 函式。以下將著重介紹 Lambda 函式中的重要程式碼片段,完整程式碼請參考相關檔案。

Lambda 處理程式類別實作

Lambda 處理程式類別實作了 RequestStreamHandler 介面,如下所示:

public class ProxyStreamHandlerLambda implements RequestStreamHandler {
    // ...
}

處理請求

處理程式方法接受 InputStreamOutputStream,以及 Context 物件:

public final void handleRequest(final InputStream inputStream,
                                final OutputStream outputStream,
                                final Context context) throws IOException {
    // ...
}

解析輸入串流

解析 InputStream 以擷取 API Gateway 傳送的事件詳細資料,並轉換為 JSONObject

JSONParser parser = new JSONParser();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
JSONObject event = (JSONObject) parser.parse(reader);

擷取路徑、查詢、標頭和主體

從事件中擷取路徑引數、查詢引數、標頭和主體:

JSONObject pathParams = (JSONObject) event.get("pathParameters");
String application = (String) pathParams.get("proxy");

JSONObject queryParams = (JSONObject) event.get("queryStringParameters");
String name = (String) queryParams.get("name");

JSONObject body = (JSONObject) parser.parse((String) event.get("body"));
String time = (String) body.get("time");

JSONObject headers = (JSONObject) event.get("headers");
String acceptHeader = (String) headers.get("Accept");

#### 內容解密:

  • 使用 JSONParser 解析 InputStream 中的 JSON 資料。
  • 從事件中擷取路徑引數、查詢引數、標頭和主體,以便進行後續處理。

建立回應

定義回應主體和標頭,並將其加入 JSONObjectresponseJson)中:

JSONObject responseBody = new JSONObject();
responseBody.put("message", greeting);

JSONObject headers = new JSONObject();
headers.put("Content-Type", "application/json");

JSONObject responseJson = new JSONObject();
responseJson.put("isBase64Encoded", false);
responseJson.put("statusCode", "200");
responseJson.put("headers", headers);
responseJson.put("body", responseBody.toString());

#### 內容解密:

  • 建立回應主體和標頭,並將其加入 responseJson 中。
  • 設定回應的 HTTP 狀態碼和標頭。
  • 將回應主體轉換為字串並加入 responseJson 中。

輸出回應

使用 OutputStreamWriter 將回應物件寫入 OutputStream

OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8");
writer.write(responseJson.toJSONString());
writer.close();

#### 內容解密:

  • 使用 OutputStreamWriter 將回應物件寫入 OutputStream
  • 設定輸出編碼為 UTF-8 以確保正確輸出。

佈署 Lambda 函式

將 Lambda 函式上傳至 S3 儲存桶,並使用 CloudFormation 範本佈署 Lambda 堆積疊。

使用 CLI 命令建立代理 API

使用 AWS CLI 命令建立 REST API,包括建立資源、新增方法、設定整合等步驟。

建立 REST API

使用 create-rest-api 命令建立 REST API。

取得根資源

使用 get-resources 命令取得根資源(/)。

新增代理資源

新增一個貪婪路徑引數({proxy+})作為代理資源。

aws apigateway create-resource \
    --rest-api-id qacob6w4v7 \
    --region us-east-1 \
    --parent-id xitaiyjnuf \
    --path-part '{proxy+}' \
    --profile admin

#### 內容解密:

  • 使用 create-resource 命令新增代理資源。
  • 設定路徑引數為 {proxy+} 以匹配任何子資源。

新增 ANY 方法

在代理資源上新增 ANY 方法以匹配任何 HTTP 方法。

aws apigateway put-method \
    --rest-api-id qacob6w4v7 \
    --resource-id k7zima \
    --http-method ANY \
    --authorization-type "NONE" \
    --region us-east-1 \
    --profile admin

#### 內容解密:

  • 使用 put-method 命令新增 ANY 方法。
  • 設定授權型別為 NONE 以允許任何請求。

設定整合

使用 put-integration 命令設定整合型別為 AWS_PROXY。

aws apigateway put-integration \
    --rest-api-id qacob6w4v7 \
    --resource-id k7zima \
    --http-method ANY \
    --type AWS_PROXY \
    --integration-http-method POST \
    --uri 'arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:<account_id>:function:lambda-for-proxy-integration/invocations' \
    --region us-east-1 \
    --profile admin

#### 內容解密:

  • 使用 put-integration 命令設定整合型別為 AWS_PROXY。
  • 設定整合 HTTP 方法為 POST 以呼叫 Lambda 函式。

使用 CloudFormation 範本建立 API

使用 CloudFormation 範本建立 API,包括建立資源、新增方法、設定整合等步驟。

建立範本

建立 CloudFormation 範本,包括版本、描述和 RestApi 資源。

新增代理資源

新增代理資源({proxy+})。

MyProxyResource:
  Type: AWS::ApiGateway::Resource
  Properties:
    RestApiId: !Ref MyRestAPI
    ParentId: !GetAtt MyRestAPI.RootResourceId
    PathPart: '{proxy+}'

#### 內容解密:

  • 使用 AWS::ApiGateway::Resource 建立代理資源。
  • 設定路徑引數為 {proxy+} 以匹配任何子資源。

新增方法定義

新增方法定義,包括設定整合型別為 AWS_PROXY 和 ANY HTTP 方法。

MyMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    AuthorizationType: NONE
    HttpMethod: ANY
    Integration:
      Type: AWS_PROXY
      IntegrationHttpMethod: POST
      Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction}/invocations'

#### 內容解密:

  • 使用 AWS::ApiGateway::Method 建立方法定義。
  • 設定授權型別為 NONE 以允許任何請求。
  • 設定整合型別為 AWS_PROXY 以呼叫 Lambda 函式。

使用 API Gateway 建置無伺服器 REST API 第 2 章節

Lambda 代理整合的理論基礎

在前面的章節中,我們已經瞭解如何使用 API Gateway 和 Lambda 來建立 REST API。在本章節中,我們將探討 Lambda 代理整合(Lambda Proxy Integration)的理論基礎,並瞭解如何定義 Lambda 函式以支援代理整合。

任意路徑引數、ANY HTTP 方法和代理整合

我們可以使用任意路徑引數 {proxy+} 來捕捉所有對資源子資源的請求。例如,/hello/{proxy+} 可以捕捉所有在 /hello/ 下的資源。ANY HTTP 方法可以用來匹配任何 HTTP 請求方法。啟用代理整合後,API Gateway 會將原始請求傳遞給 Lambda 函式。

RequestStreamHandler 與 RequestHandler

RequestHandler<I, O> 介面接受並傳回 POJO(Plain Old Java Object)。JSON 負載會被對映到請求 POJO,而回應 POJO 則會被對映到 JSON 回應。RequestStreamHandler 則用於低階請求處理。處理方法提供了對 InputStream 的輸入存取和對 OutputStream 的輸出存取。RequestStreamHandler 通常與代理整合一起使用。

Lambda 函式的輸入和輸出格式

在使用 API Gateway 的代理整合時,API Gateway 會將 HTTP 請求以特定的格式傳遞給 Lambda 函式。同樣地,API Gateway 也期望 Lambda 函式的輸出遵循特定的格式。

輸入格式

{
  "resource": "資源路徑",
  "path": "路徑引數",
  "httpMethod": "請求方法名稱",
  "headers": { 請求標頭 },
  "queryStringParameters": { 查詢字串引數 },
  "pathParameters": { 路徑引數 },
  "stageVariables": { 可用的階段變數 },
  "requestContext": { 請求上下文,包含授權者傳回的鍵值對 },
  "body": "請求負載的 JSON 字串",
  "isBase64Encoded": "布林值,表示請求負載是否為 Base64 編碼"
}

輸出格式

{
  "isBase64Encoded": true|false,
  "statusCode": HTTP 狀態碼,
  "headers": { 標頭名稱: 標頭值 的鍵值對 },
  "body": "回應內容"
}

更多資訊

在本章中,我們學習瞭如何使用 API Gateway 和 Lambda 建立 REST API,包括模擬整合、Lambda 整合和 Lambda 代理整合。您也可以嘗試使用 HTTP 和 HTTP 代理整合,並將 API Gateway 與其他 AWS 服務整合。

相關資源

您可以從以下連結瞭解更多關於 API Gateway 的資訊: