CloudFormation 提供了簡潔有效的方式來定義和管理 AWS 基礎設施,本文將示範如何使用 CloudFormation 範本建立一個與 Lambda 函式整合的無伺服器 REST API。首先,我們會定義 CloudFormation 範本的基本結構,接著使用 AWS::ApiGateway::RestApi 資源建立 REST API,並使用 AWS::ApiGateway::Method 定義資源和方法,例如 POST 方法。其中,Integration 屬性定義了與後端 Lambda 函式的整合方式,包含整合型別、HTTP 方法和 URI 等。為了確保 API Gateway 能夠呼叫 Lambda 函式,我們需要使用 AWS::Lambda::Permission 資源授予必要的許可權。最後,我們可以使用 Postman 等工具測試已佈署的 API。除了基本 API 建立流程外,本文也探討如何使用對映範本和 JSONPath 表示式,靈活地轉換 API 請求和回應的資料格式,以符合前端和後端服務的需求。同時,我們也示範如何使用 JSON Schema 定義模型,並在 API Gateway 中使用模型來驗證請求負載,確保 API 的安全性及穩定性。

使用 CloudFormation 範本建立無伺服器 REST API

在前面的章節中,我們已經討論過一些 AWS API Gateway 和 AWS Lambda 的基本概念。現在,我們將探討如何使用 CloudFormation 範本建立一個簡單的 REST API,並與 Lambda 函式整合。

建立 CloudFormation 範本

首先,我們需要定義 CloudFormation 範本的基本結構,包括 AWSTemplateFormatVersion 和描述資訊。

定義 REST API

接下來,我們使用 AWS::ApiGateway::RestApi 資源建立 REST API。

定義資源和方法

我們定義了一個名為 lambdagreeting 的資源,並使用 AWS::ApiGateway::Method 定義了一個 POST 方法。

MyMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    AuthorizationType: NONE
    HttpMethod: POST
    Integration:
      Type: AWS
      IntegrationHttpMethod: POST
      IntegrationResponses:
        - StatusCode: 200
      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
    ResourceId: !Ref GreetingResource
    RestApiId: !Ref MyRestAPI
    MethodResponses:
      - StatusCode: 200

內容解密:

  • MyMethod:定義了一個名為 MyMethod 的 API Gateway 方法資源。
  • Type: 指定資源型別為 AWS::ApiGateway::Method
  • Properties:定義了該方法的屬性。
    • AuthorizationType: 指定授權型別為 NONE,表示無需授權即可呼叫此方法。
    • HttpMethod: 指定 HTTP 方法為 POST
    • Integration: 定義與後端服務的整合。
      • Type: 指定整合型別為 AWS,表示與 AWS 服務整合。
      • IntegrationHttpMethod: 指定與後端服務互動的 HTTP 方法為 POST
      • IntegrationResponses: 定義整合回應。
        • StatusCode: 指定傳回的 HTTP 狀態碼為 200,表示成功。
      • Uri: 指定後端 Lambda 函式的呼叫地址,使用 !Sub 函式進行字串替換,動態生成 ARN。
    • ResourceId: 參照名為 GreetingResource 的資源 ID,表示此方法繫結的資源。
    • RestApiId: 參照名為 MyRestAPI 的 REST API ID,表示此方法所屬的 API。
    • MethodResponses: 定義方法的回應。
      • StatusCode: 指定傳回的 HTTP 狀態碼為 200

佈署 API 和授予 Lambda 呼叫許可權

我們使用 AWS::ApiGateway::Deployment 將 API 佈署到 dev 環境,並使用 AWS::Lambda::Permission 授予 API Gateway 呼叫 Lambda 的許可權。

LambdaInvokePermission:
  Type: AWS::Lambda::Permission
  Properties:
    FunctionName: !ImportValue LambdaForApiGateway
    Action: 'lambda:InvokeFunction'
    Principal: apigateway.amazonaws.com
    SourceArn: !Sub
      - 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${API_ID}/*/POST/lambdagreeting'
      - API_ID: !Ref MyRestAPI

內容解密:

  • LambdaInvokePermission:定義了一個允許 API Gateway 呼叫 Lambda 函式的許可權資源。
  • Type: 指定資源型別為 AWS::Lambda::Permission
  • Properties:定義了該許可權的屬性。
    • FunctionName: 指定被呼叫的 Lambda 函式名稱,透過 !ImportValue 匯入外部值 LambdaForApiGateway 取得。
    • Action: 指定允許執行的動作是 lambda:InvokeFunction,即呼叫 Lambda 函式。
    • Principal: 指定執行此動作的主體是 apigateway.amazonaws.com,即 API Gateway 服務。
    • SourceArn: 指定允許呼叫 Lambda 的源 ARN,使用 !Sub 函式替換 ${AWS::Region}${AWS::AccountId}${API_ID},確保 ARN 的正確性。

使用 Postman 測試 API

我們可以使用 Postman 這樣的工具來測試我們的 API。首先,安裝 Postman,然後建立一個新的請求,選擇 POST 方法,並設定正確的端點 URL 和 JSON 載荷。

Passthrough 行為

在我們的示例中,我們沒有定義請求範本,因此 API Gateway 將請求主體直接傳遞給 Lambda。這是由於 passthrough 行為的預設設定。我們可以透過設定 PassthroughBehavior 屬性來控制這種行為。

有效的 passthrough 行為值包括:

  • WHEN_NO_MATCH:當沒有匹配的範本時,允許傳遞請求主體。
  • WHEN_NO_TEMPLATES:僅當沒有為任何內容型別定義範本時才允許傳遞請求主體。如果至少為一種內容型別定義了範本,則其他內容型別將被拒絕,並傳回 HTTP 415 狀態碼。
  • NEVER:拒絕所有未對映的內容型別,並傳回 HTTP 415 狀態碼。

使用對映範本進行請求與回應的轉換

Amazon API Gateway允許我們使用對映範本,將傳入的請求對映到Lambda所需的格式,並將Lambda的回應對映到客戶端所需的格式。API Gateway的對映範本根據Apache Velocity範本語言(VTL)和JSONPath表示式。

準備工作

  • 擁有一個有效的AWS帳戶。
  • 按照第一章節的指示,設定Java、Maven、父專案serverless-cookbook-parent-aws-java和AWS CLI。
  • 重用之前建立的Lambda函式(如果尚未建立,請按照相關章節的指示進行建立、構建和佈署)。

請求對映

假設客戶端傳送的POST請求主體如下:

{
  "user": {
    "name": "Heartin"
  },
  "greeting": {
    "time": "Morning"
  }
}

Lambda函式期望的請求格式如下:

{
  "name": "Heartin",
  "time": "Morning"
}

我們可以使用以下對映範本將客戶端的請求對映到Lambda期望的格式:

{
  "name": $input.json('$.user.name'),
  "time": $input.json('$.greeting.time')
}

程式碼解析:

此對映範本使用$input.json函式從原始請求中提取特定欄位的值。其中:

  • $input.json('$.user.name')提取user物件下的name欄位值。
  • $input.json('$.greeting.time')提取greeting物件下的time欄位值。

回應對映

Lambda函式傳回的回應如下:

{
  "message": "Good Morning, Heartin"
}

客戶端期望的回應格式如下:

{
  "greeting": "Good Morning, Heartin"
}

我們可以使用以下對映範本將Lambda的回應對映到客戶端期望的格式:

{
  "greeting": $input.json('$.message')
}

程式碼解析:

此對映範本同樣使用$input.json函式,將Lambda回應中的message欄位值賦予給客戶端回應中的greeting欄位。

使用CLI命令建立API

  1. 使用create-rest-api命令建立REST API。
  2. 使用get-resources命令取得根資源(/)。
  3. 使用create-resource命令建立路徑部分(lambdagreeting)。
  4. 使用put-method命令建立POST方法。
  5. 使用put-method-response命令設定回應狀態碼。
  6. 使用put-integration命令設定請求對映範本,例如:
aws apigateway put-integration \
  --rest-api-id y3yftanqp7 \
  --resource-id e4w7ka \
  --http-method POST \
  --type AWS \
  --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-api-gateway/invocations' \
  --request-templates '{ "application/json" : "{ \"name\" : $input.json(\'$.user.name\'), \"time\" : $input.json(\'$.greeting.time\') }" }' \
  --passthrough-behavior WHEN_NO_TEMPLATES \
  --region us-east-1 \
  --profile admin
  1. 使用put-integration-response命令設定回應對映範本,例如:
aws apigateway put-integration-response \
  --rest-api-id y3yftanqp7 \
  --resource-id e4w7ka \
  --http-method POST \
  --status-code 200 \
  --region us-east-1 \
  --selection-pattern "" \
  --response-templates '{ "application/json" : "{ \"greeting\" : $input.json(\'$.message\') }" }' \
  --profile admin
  1. 使用create-deployment命令佈署API到某個階段。

使用CloudFormation範本建立API

在CloudFormation範本中,主要的變更在於AWS::ApiGateway::Method資源型別的Integration屬性中,例如:

Integration:
  Type: AWS
  IntegrationHttpMethod: POST
  PassthroughBehavior: WHEN_NO_TEMPLATES
  RequestTemplates:
    application/json: "{ \"name\" : $input.json('$.user.name'), \"time\" : $input.json('$.greeting.time') }"
  IntegrationResponses:
    - StatusCode: 200
      ResponseTemplates:
        application/json: "{ \"greeting\" : $input.json('$.message') }"

測試API

可以使用Postman或其他REST/HTTP客戶端測試API。

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

對映範本與變數

在 API Gateway 中,您可以使用根據 Apache VTL 和 JSONPath 表示式的 body 對映範本來對映請求和回應。API Gateway 提供的 $input 變數代表輸入負載(根據情況可以是請求負載或回應負載)以及範本可用的引數。函式 $input.json() 根據 JSONPath 表示式檢索 JSON 的一部分。在 JSONPath 中,$ 代表最外層的 JSON 物件。例如,$input.json('$.user.name') 傳回 user 物件內的 name 欄位的值,而 $input.json('$.greeting.time') 傳回 greeting 物件內的 time 欄位的值。

使用 #set 定義變數

您可以使用 #set 在範本內定義變數。原始範本如下:

{
  "name": $input.json('$.user.name'),
  "time": $input.json('$.greeting.time')
}

使用 #set 重寫後的範本如下:

#set($inputRoot = $input.path('$'))
{
  "name": "$inputRoot.user.name",
  "time": "$inputRoot.greeting.time"
}

在 AWS Management Console 中,API Gateway 自動生成的變數名稱是 $inputRoot,但您可以自由使用其他名稱。

Apache Velocity 語言

Apache Velocity 是一種根據 Java 的範本引擎。它最初是為網頁設計師開發的,以便在不瞭解 Java 程式語言的情況下存取 Java 物件。目前,Velocity 被用作各種使用案例的範本引擎,例如生成網頁、SQL、PostScript 等。API Gateway 使用它進行對映範本。您可以透過 http://velocity.apache.org 瞭解更多關於 Velocity 語言的資訊。

JSONPath 表示式

JSONPath 表示式與 JSON 物件一起使用,類別似於 XPath 表示式與 XML 檔案一起使用。 $ 代表根級物件, @ 代表當前物件。JSONPath 表示式可以使用點表示法或方括號表示法。您可以透過 http://goessner.net/articles/JsonPath 瞭解更多關於 JSONPath 的資訊。

使用模型驗證請求負載

在本文中,我們將使用模型為請求負載新增驗證功能。我們將使用 JSON schema 草案語言定義一個模型,然後使用它來驗證我們的負載。我們將重用在「使用 Lambda 整合建立您的第一個 API」配方中使用的 Lambda。

請求資料格式

我們將使用與前一個配方相同的請求格式:

{
  "user": {
    "name": "Heartin"
  },
  "greeting": {
    "time": "Morning"
  }
}

使用 CLI 命令建立 API

首先,讓我們使用 AWS CLI 命令建立 REST API。我們不會展示如何使用已經在前面的配方中討論過的命令。完整的命令將在程式碼檔案中提供:

  1. 使用 apigateway 子命令 create-rest-api 在 API Gateway 中建立 REST API。
  2. 使用 apigateway 子命令 get-resources 取得我們的 API 的根資源( / )。
  3. 使用 apigateway 子命令 create-resource 建立我們的路徑部分 lambdagreeting
  4. 使用 apigateway 子命令 create-model 為我們的 JSON 建立模型架構:
aws apigateway create-model \
  --rest-api-id dqnqdyb3z2 \
  --name 'greetingRequestModel' \
  --description 'Greeting Request Model' \
  --content-type 'application/json' \
  --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"]
  }' \
  --profile admin

程式碼解析:

此 CLI 命令用於在 API Gateway 中建立一個名為 greetingRequestModel 的模型,該模型定義了請求的 JSON 結構,包括必要的欄位 usergreeting

檢視更多

閱讀更多關於建立模型和對映範本的資訊,請存取 https://docs.aws.amazon.com/apigateway/latest/developerguide/models-mappings.html。API Gateway 對映範本中可用的變數請參考 https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html