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
- 使用
create-rest-api命令建立REST API。 - 使用
get-resources命令取得根資源(/)。 - 使用
create-resource命令建立路徑部分(lambdagreeting)。 - 使用
put-method命令建立POST方法。 - 使用
put-method-response命令設定回應狀態碼。 - 使用
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
- 使用
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
- 使用
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。我們不會展示如何使用已經在前面的配方中討論過的命令。完整的命令將在程式碼檔案中提供:
- 使用
apigateway子命令create-rest-api在 API Gateway 中建立 REST API。 - 使用
apigateway子命令get-resources取得我們的 API 的根資源(/)。 - 使用
apigateway子命令create-resource建立我們的路徑部分lambdagreeting。 - 使用
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 結構,包括必要的欄位 user 和 greeting。
檢視更多
閱讀更多關於建立模型和對映範本的資訊,請存取 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。