AWS Lambda 與 API Gateway 整合

深入解析 AWS Lambda 與 API Gateway 的整合方式,涵蓋 REST API 建立、Lambda 代理整合、部署階段管理及 CORS 設定

整合概念

AWS Lambda 與 API Gateway 的整合是建構無伺服器應用程式的核心架構。API Gateway 作為前端入口,接收來自客戶端的 HTTP 請求,並將這些請求轉發給 Lambda 函數處理,最後將回應返回給客戶端。

整合架構圖

1
2
3
客戶端 → API Gateway → Lambda 函數 → 後端服務(可選)
   ↑                        ↓
   ←─────── 回應 ─────────←

整合類型

API Gateway 提供兩種主要的 Lambda 整合方式:

整合類型說明適用場景
Lambda 代理整合API Gateway 將完整請求傳遞給 Lambda需要完整請求資訊時
Lambda 自訂整合可自訂請求/回應的映射模板需要轉換資料格式時

整合優勢

  • 完全無伺服器:無需管理任何基礎設施
  • 自動擴展:根據請求量自動調整容量
  • 成本效益:只為實際使用量付費
  • 高可用性:內建多可用區支援

建立 REST API

步驟一:建立 API Gateway

  1. 登入 AWS 管理控制台
  2. 搜尋並進入 API Gateway 服務
  3. 點擊「建立 API」
  4. 選擇「REST API」(非私有)
  5. 點擊「建置」

步驟二:設定 API 基本資訊

1
2
3
API 名稱:my-serverless-api
描述:我的無伺服器 API
端點類型:區域(Regional)

步驟三:建立資源

  1. 在「資源」面板中點擊「建立資源」
  2. 設定資源路徑:
1
2
資源名稱:users
資源路徑:/users
  1. 點擊「建立資源」

步驟四:建立方法

  1. 選擇剛建立的資源
  2. 點擊「建立方法」
  3. 選擇 HTTP 方法(例如:GET、POST)
1
2
3
4
方法類型:GET
整合類型:Lambda 函數
Lambda 代理整合:啟用
Lambda 函數:選擇您的函數

使用 AWS CLI 建立 API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 建立 REST API
aws apigateway create-rest-api \
  --name "my-serverless-api" \
  --description "我的無伺服器 API" \
  --endpoint-configuration types=REGIONAL

# 取得 API ID 和根資源 ID
API_ID=$(aws apigateway get-rest-apis --query "items[?name=='my-serverless-api'].id" --output text)
ROOT_ID=$(aws apigateway get-resources --rest-api-id $API_ID --query "items[?path=='/'].id" --output text)

# 建立資源
aws apigateway create-resource \
  --rest-api-id $API_ID \
  --parent-id $ROOT_ID \
  --path-part "users"

Lambda 代理整合

Lambda 代理整合是最常用的整合方式,它會將完整的 HTTP 請求資訊傳遞給 Lambda 函數。

請求格式

當使用 Lambda 代理整合時,Lambda 函數會收到以下格式的事件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "resource": "/users",
  "path": "/users",
  "httpMethod": "GET",
  "headers": {
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  "queryStringParameters": {
    "id": "123"
  },
  "pathParameters": {
    "userId": "456"
  },
  "body": "{\"name\": \"John\"}",
  "isBase64Encoded": false,
  "requestContext": {
    "accountId": "123456789012",
    "apiId": "abcdef1234",
    "stage": "prod",
    "requestId": "request-id",
    "identity": {
      "sourceIp": "192.168.1.1"
    }
  }
}

回應格式

Lambda 函數必須返回符合以下格式的回應:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import json

def lambda_handler(event, context):
    # 處理請求邏輯
    users = [
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"}
    ]

    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json",
            "Access-Control-Allow-Origin": "*"
        },
        "body": json.dumps({
            "message": "成功",
            "data": users
        })
    }

處理不同 HTTP 方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import json

def lambda_handler(event, context):
    http_method = event['httpMethod']

    if http_method == 'GET':
        return handle_get(event)
    elif http_method == 'POST':
        return handle_post(event)
    elif http_method == 'PUT':
        return handle_put(event)
    elif http_method == 'DELETE':
        return handle_delete(event)
    else:
        return {
            "statusCode": 405,
            "body": json.dumps({"error": "方法不允許"})
        }

def handle_get(event):
    # 處理 GET 請求
    query_params = event.get('queryStringParameters', {}) or {}
    user_id = query_params.get('id')

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"userId": user_id})
    }

def handle_post(event):
    # 處理 POST 請求
    body = json.loads(event.get('body', '{}'))

    return {
        "statusCode": 201,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"message": "資源已建立", "data": body})
    }

def handle_put(event):
    # 處理 PUT 請求
    return {
        "statusCode": 200,
        "body": json.dumps({"message": "資源已更新"})
    }

def handle_delete(event):
    # 處理 DELETE 請求
    return {
        "statusCode": 204,
        "body": ""
    }

處理路徑參數

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import json

def lambda_handler(event, context):
    # 取得路徑參數
    path_parameters = event.get('pathParameters', {}) or {}
    user_id = path_parameters.get('userId')

    if not user_id:
        return {
            "statusCode": 400,
            "body": json.dumps({"error": "缺少 userId 參數"})
        }

    # 根據 userId 查詢使用者
    user = get_user_by_id(user_id)

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps(user)
    }

部署階段

API Gateway 使用「階段」(Stage)來管理 API 的不同版本和環境。

建立部署

  1. 在 API Gateway 控制台中選擇您的 API
  2. 點擊「部署 API」
  3. 選擇或建立新的階段
1
2
階段名稱:prod
描述:生產環境

常見階段規劃

階段用途說明
dev開發環境開發人員測試用
staging預備環境上線前最終測試
prod生產環境對外服務

使用 AWS CLI 部署

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 建立部署
aws apigateway create-deployment \
  --rest-api-id $API_ID \
  --stage-name prod \
  --description "生產環境部署"

# 取得階段資訊
aws apigateway get-stage \
  --rest-api-id $API_ID \
  --stage-name prod

階段變數

階段變數可用於在不同環境中設定不同的參數:

1
2
3
4
5
# 設定階段變數
aws apigateway update-stage \
  --rest-api-id $API_ID \
  --stage-name prod \
  --patch-operations op=replace,path=/variables/lambdaAlias,value=PROD

在 Lambda 整合中使用階段變數:

1
Lambda 函數 ARN:arn:aws:lambda:region:account:function:myFunction:${stageVariables.lambdaAlias}

取得 API 端點

部署後,您可以取得 API 的調用 URL:

1
https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/

例如:

1
https://abcdef1234.execute-api.ap-northeast-1.amazonaws.com/prod/users

節流設定

可以在階段層級設定請求節流:

1
2
3
4
5
6
aws apigateway update-stage \
  --rest-api-id $API_ID \
  --stage-name prod \
  --patch-operations \
    op=replace,path=/throttling/rateLimit,value=1000 \
    op=replace,path=/throttling/burstLimit,value=500

CORS 設定

跨來源資源共用(CORS)是瀏覽器安全機制,當您的前端應用程式需要呼叫不同網域的 API 時,必須正確設定 CORS。

啟用 CORS 的步驟

方法一:使用控制台啟用

  1. 在 API Gateway 控制台中選擇資源
  2. 點擊「啟用 CORS」
  3. 設定 CORS 選項:
1
2
3
Access-Control-Allow-Origin:*
Access-Control-Allow-Headers:Content-Type,X-Amz-Date,Authorization,X-Api-Key
Access-Control-Allow-Methods:GET,POST,PUT,DELETE,OPTIONS
  1. 點擊「儲存」

方法二:手動設定 OPTIONS 方法

  1. 為資源建立 OPTIONS 方法
  2. 整合類型選擇「Mock」
  3. 在方法回應中設定標頭
1
2
3
4
5
狀態碼:200
回應標頭:
  - Access-Control-Allow-Headers
  - Access-Control-Allow-Methods
  - Access-Control-Allow-Origin

Lambda 函數中的 CORS 標頭

在 Lambda 代理整合中,您需要在回應中包含 CORS 標頭:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import json

def lambda_handler(event, context):
    # CORS 標頭
    cors_headers = {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
        "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS"
    }

    # 處理 OPTIONS 預檢請求
    if event['httpMethod'] == 'OPTIONS':
        return {
            "statusCode": 200,
            "headers": cors_headers,
            "body": ""
        }

    # 處理實際請求
    try:
        result = process_request(event)
        return {
            "statusCode": 200,
            "headers": {
                "Content-Type": "application/json",
                **cors_headers
            },
            "body": json.dumps(result)
        }
    except Exception as e:
        return {
            "statusCode": 500,
            "headers": cors_headers,
            "body": json.dumps({"error": str(e)})
        }

限制特定網域

在生產環境中,建議限制允許的來源網域:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
ALLOWED_ORIGINS = [
    "https://example.com",
    "https://www.example.com",
    "https://app.example.com"
]

def get_cors_headers(event):
    origin = event.get('headers', {}).get('origin', '')

    if origin in ALLOWED_ORIGINS:
        return {
            "Access-Control-Allow-Origin": origin,
            "Access-Control-Allow-Credentials": "true",
            "Access-Control-Allow-Headers": "Content-Type,Authorization",
            "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS"
        }

    return {}

使用 AWS SAM 設定 CORS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Api:
    Cors:
      AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
      AllowHeaders: "'Content-Type,Authorization'"
      AllowOrigin: "'*'"

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: prod
      Cors:
        AllowMethods: "'*'"
        AllowHeaders: "'*'"
        AllowOrigin: "'*'"

  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.lambda_handler
      Runtime: python3.11
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /users
            Method: GET
            RestApiId: !Ref MyApi

最佳實踐

1. 安全性設定

  • 使用 IAM 角色或 Lambda 授權方進行身份驗證
  • 設定 API 金鑰和使用計畫
  • 啟用 AWS WAF 保護 API

2. 效能優化

  • 使用 API Gateway 快取減少 Lambda 調用
  • 適當設定 Lambda 記憶體和逾時
  • 考慮使用 Provisioned Concurrency

3. 監控與日誌

  • 啟用 CloudWatch Logs 進行除錯
  • 設定 X-Ray 追蹤請求路徑
  • 建立 CloudWatch 警示監控錯誤率

4. 版本控制

  • 使用 API Gateway 階段管理不同環境
  • Lambda 函數使用版本和別名
  • 實作藍綠部署策略

結論

AWS Lambda 與 API Gateway 的整合提供了強大且靈活的無伺服器 API 解決方案。透過正確設定 REST API、Lambda 代理整合、部署階段和 CORS,您可以建構高效能、可擴展且安全的 API 服務。掌握這些核心概念後,您將能夠快速開發和部署各種無伺服器應用程式。

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy