1. 概述
AWS Lambda 是亚马逊提供的一种无服务器(Serverless)计算服务,开发者无需管理服务器即可运行代码。
在之前的两篇文章中,我们已经介绍了 如何用 Java 创建 AWS Lambda 函数,以及 如何在 Lambda 中访问 DynamoDB。
本文将重点讲解:如何通过 AWS API Gateway 将 Lambda 函数发布为 REST 接口。
我们将深入探讨以下内容:
- API Gateway 的核心概念与术语
- 使用 Lambda 代理集成(Lambda Proxy Integration)将 Lambda 与 API Gateway 集成
- 创建 API 的结构,并将资源映射到 Lambda 函数
- API 的部署与测试
✅ 适合有一定 AWS 使用经验的开发者,避免基础概念啰嗦。
2. 基础概念与术语
API Gateway 是一项 完全托管的服务,帮助开发者创建、发布、维护、监控和保护任意规模的 API。
你可以通过它暴露后端服务,比如 Lambda 函数、其他 AWS 服务(如 EC2、S3、DynamoDB)或任意 HTTP 接口,形成统一的 RESTful 接口。
主要特性包括:
- 流量管理(限流、缓存)
- 认证与权限控制
- 监控与日志
- API 版本管理
- 请求限速,防止恶意攻击
和 Lambda 一样,API Gateway 自动弹性伸缩,按调用次数计费。
详细文档见:官方文档
2.1 关键术语
- API Gateway:AWS 提供的服务,用于创建和管理 RESTful 接口,可集成 Lambda、HTTP 接口等后端。
- API Gateway API:一个 API 由一组资源(Resources)和方法(Methods)组成。每个资源可以绑定多个 HTTP 方法(GET、PUT、POST 等),且方法必须唯一。
- Deployment(部署)与 Stage(阶段):要发布 API,必须创建部署并关联一个 Stage。Stage 相当于 API 的快照,支持多环境并行,比如
dev
、test
、prod
或版本化的v1
、v2
。 - Lambda 代理集成(Lambda Proxy Integration):一种简化集成方式。API Gateway 将整个 HTTP 请求打包成一个 JSON 传给 Lambda,Lambda 返回的 JSON 再由 API Gateway 转为 HTTP 响应。
⚠️ 使用代理集成后,Lambda 函数需要处理完整的请求结构,但开发更简单,推荐新手使用。
3. 依赖项
除了之前文章中使用的依赖,我们还需要 json-simple
库来处理 JSON:
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
✅ 依赖简单,没有引入 Spring 或复杂框架,保持轻量。
4. 开发与部署 Lambda 函数
本节将用 Java 实现两个 Lambda 函数,通过 AWS 控制台部署并测试。
我们设计两个功能:
- 函数1:接收 PUT 请求的 JSON 载荷,存入 DynamoDB
- 函数2:通过路径参数或查询参数获取 Person 数据
为简化,我们将两个处理逻辑放在同一个 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
}
📌 注意:构造函数接收 JSON 字符串,直接反序列化为 Person
对象。
4.2 RequestHandler 实现
我们实现 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 {
// 处理 PUT 请求
}
public void handleGetByParam(
InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
// 处理 GET 请求(路径或查询参数)
}
}
📌 虽然接口只强制实现 handleRequest
,但我们可以在类中定义多个方法。部署时通过不同函数名绑定不同入口。
💡 环境变量 TABLE_NAME
用于解耦配置,部署时注入。
4.3 函数1:处理请求体(PUT)
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();
}
📌 核心逻辑三步走:
- 从
inputStream
解析 JSON,提取body
字段(字符串) - 构造
Person
对象并写入 DynamoDB - 构造响应 JSON,包含
statusCode
、headers
和body
(字符串)
⚠️ 关键点:API Gateway 要求 body
字段必须是字符串(即使是 JSON 内容)。Lambda 返回时也必须是字符串,否则会报错。
4.4 函数2:处理路径/查询参数
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();
}
📌 逻辑流程:
- 检查
pathParameters
或queryStringParameters
是否包含id
- 解析
id
并查询 DynamoDB - 构造响应,注意
body
仍为字符串
📌 官方文档参考:
4.5 打包代码
使用 Maven 打包:
mvn clean package shade:shade
生成的 JAR 文件位于 target/
目录。
4.6 创建 DynamoDB 表
创建名为 Person
的表,主键为 id
(Number 类型)。
操作方式参考:AWS Lambda 使用 DynamoDB
4.7 控制台部署 Lambda
步骤如下:
- 进入 AWS Lambda 控制台
- 创建函数
StorePersonFunction
,上传 JAR,入口类为APIDemoHandler::handleRequest
- 创建函数
GetPersonByHTTPParamFunction
,入口为APIDemoHandler::handleGetByParam
- 为两个函数设置环境变量:
TABLE_NAME=Person
⚠️ 记得给 Lambda 添加执行角色(IAM 权限),允许访问 DynamoDB。
4.8 测试 Lambda 函数
在控制台测试时,需使用 API Gateway AWS Proxy 模板,确保输入格式正确。
测试 StorePersonFunction
的事件:
{
"body": "{\"id\": 1, \"name\": \"John Doe\"}"
}
✅ 预期响应:
{
"isBase64Encoded": false,
"headers": {
"x-custom-header": "my custom header value"
},
"body": "{\"message\":\"New item created\"}",
"statusCode": 200
}
测试 GetPersonByHTTPParamFunction
(路径参数):
{
"pathParameters": {
"id": "1"
}
}
测试查询参数:
{
"queryStringParameters": {
"id": "1"
}
}
✅ 两种输入应返回类似响应(body
为字符串)。
5. 创建与测试 API
5.1 创建 API
- 进入 API Gateway 控制台
- 点击 “Get Started” → “New API”
- 命名 API 为
TestAPI
,点击 “Create API”
5.2 为函数1创建资源(PUT)
- 选择根资源 → “Create Resource”
- 名称:
Persons
- 路径:默认(即
/persons
)
- 名称:
- 选中
/persons
→ “Create Method” → 选择PUT
- 配置集成:
- 集成类型:Lambda Function
- ✅ 启用 “Lambda 代理集成”
- 选择区域和函数:
StorePersonFunction
- 点击 “Save”,确认添加权限
5.3 为函数2创建路径参数资源(GET /persons/{id})
- 选中
/persons
→ “Create Resource”- 名称:
Person
- 路径:
{id}
(必须匹配代码中的参数名)
- 名称:
- 选中
/persons/{id}
→ “Create Method” → 选择GET
- 集成配置同上,函数选择
GetPersonByHTTPParamFunction
- 保存并授权
5.4 为函数2创建查询参数方法(GET /persons?id=)
- 选中
/persons
→ “Create Method” → 选择GET
- 配置 Lambda 代理集成,绑定
GetPersonByHTTPParamFunction
- 保存后,进入 “Method Request” 配置:
- 在 “URL Query String Parameters” 中添加
id
- 设置为 “Required”
- 配置 “Request Validator”:启用查询参数和 Header 校验
- 在 “URL Query String Parameters” 中添加
📌 注意:查询参数无需创建新资源,直接在 /persons
上添加 GET 方法即可。
5.5 控制台测试 API
在 API Gateway 控制台点击 “Test” 按钮:
- PUT /persons:在 “Request Body” 输入:
{"id": 2, "name": "Jane Doe"}
- GET /persons/2:在
{id}
字段填2
- GET /persons?id=2:在 “Query Strings” 输入
id=2
✅ 查看响应状态码与内容是否符合预期。
5.6 部署 API
- 选择 API → “Actions” → “Deploy API”
- 选择
[New Stage]
,命名test
,填写描述 - 点击 “Deploy”
部署成功后,控制台会显示调用 URL,例如:
https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test
5.7 调用接口
使用 curl
测试:
创建用户:
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"}'
通过路径参数查询:
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/3' \
-H 'content-type: application/json'
通过查询参数查询:
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons?id=3' \
-H 'content-type: application/json'
✅ 三个接口应分别返回预期结果。
6. 总结
本文实战演示了如何通过 API Gateway + Lambda 代理集成 快速暴露 REST 接口。
关键点回顾:
- ✅ 使用 Lambda 代理集成,简化前后端数据映射
- ✅
body
字段必须为字符串,别踩坑 - ✅ 支持路径参数、查询参数、请求体等多种输入方式
- ✅ 多 Stage 支持,便于环境隔离
所有代码已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/aws-modules/aws-lambda-modules
💡 建议:生产环境可结合 CloudFront 做缓存,或使用自定义域名提升可读性。