教程:Aurora Serverless - AWS AppSync
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

教程:Aurora Serverless

AWS AppSync 提供了一个数据源,用于针对已使用数据 API 启用的 Amazon Aurora Serverless 集群执行 SQL 命令。您可以使用 AppSync 解析程序通过 GraphQL 查询、更改和订阅对数据 API 执行 SQL 语句。

创建集群

在将 RDS 数据源添加到 AppSync 之前,您必须先在 Aurora Serverless 集群上启用数据 API 并使用 AWS Secrets Manager 配置密钥。您可以首先使用 AWS CLI 创建 Aurora Serverless 集群:

aws rds create-db-cluster --db-cluster-identifier http-endpoint-test --master-username USERNAME \ --master-user-password COMPLEX_PASSWORD --engine aurora --engine-mode serverless \ --region us-east-1

这将为集群返回一个 ARN。

使用上一步中的 USERNAME 和 COMPLEX_PASSWORD,通过 AWS Secrets Manager 控制台或通过包含类似于以下内容的输入文件的 CLI 创建密钥:

{ "username": "USERNAME", "password": "COMPLEX_PASSWORD" }

将此内容作为参数传递到 AWS CLI:

aws secretsmanager create-secret --name HttpRDSSecret --secret-string file://creds.json --region us-east-1

这将为密钥返回 ARN。

记下稍后在创建数据源时在 AppSync 控制台中使用的 Aurora Serverless 集群和密钥的 ARN。

启用数据 API

您可以通过按照 RDS 文档中的说明操作来在您的集群上启用数据 API。在将数据 API 作为 AppSync 数据源添加之前,必须先启用它。

创建数据库和表

在启用数据 API 后,您可以确保它在 AWS CLI 中使用 aws rds-data execute-statement 命令。这将确保在将 Aurora Serverless 集群添加到 AppSync API 之前已进行正确配置。首先使用 --sql 参数创建一个名为 TESTDB 的数据库,如下所示:

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \ --region us-east-1 --sql "create DATABASE TESTDB"

如果运行无误,请使用创建表 命令添加一个表:

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \ --region us-east-1 \ --sql "create table Pets(id varchar(200), type varchar(200), price float)" --database "TESTDB"

如果一切运行正常,您可以继续在 AppSync API 中将集群添加为数据源。

GraphQl 架构

现在,您的 Aurora Serverless 数据 API 已通过表启动并运行,我们将创建一个 GraphQL 架构并附加用于执行更改和订阅的解析程序。在 AWS AppSync 控制台中创建一个新 API 并导航到架构页,然后输入以下内容:

type Mutation { createPet(input: CreatePetInput!): Pet updatePet(input: UpdatePetInput!): Pet deletePet(input: DeletePetInput!): Pet } input CreatePetInput { type: PetType price: Float! } input UpdatePetInput { id: ID! type: PetType price: Float! } input DeletePetInput { id: ID! } type Pet { id: ID! type: PetType price: Float } enum PetType { dog cat fish bird gecko } type Query { getPet(id: ID!): Pet listPets: [Pet] listPetsByPriceRange(min: Float, max: Float): [Pet] } schema { query: Query mutation: Mutation }

保存您的架构并导航到数据源页面,然后创建新的数据源。为数据源类型选择关系数据库,并提供一个友好名称。使用您在上一步中创建的数据库名称以及用来创建它的集群 ARN。对于角色,您可以让 AppSync 创建新角色,也可以使用与以下内容类似的策略创建角色:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-data:DeleteItems", "rds-data:ExecuteSql", "rds-data:ExecuteStatement", "rds-data:GetItems", "rds-data:InsertItems", "rds-data:UpdateItems" ], "Resource": [ "arn:aws:rds:us-east-1:123456789012:cluster:mydbcluster", "arn:aws:rds:us-east-1:123456789012:cluster:mydbcluster:*" ] }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret", "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret:*" ] } ] }

请注意,此策略中有两个需要获得角色访问权的语句。第一个资源是您的 Aurora Serverless 集群,第二个资源是您的 AWS Secrets Manager ARN。在单击 Create (创建) 之前,您需要在 AppSync 数据源配置中提供这两个 ARN。

配置解析程序

现在,我们有一个有效的 GraphQL 架构和一个 RDS 数据源,我们可以将解析程序附加到架构上的 GraphQL 字段。我们的 API 将提供以下功能:

  1. 通过 Mutation.createPet 字段创建宠物

  2. 通过 Mutation.updatePet 字段更新宠物

  3. 通过 Mutation.deletePet 字段删除宠物

  4. 通过 Query.getPet 字段获取单个宠物

  5. 通过 Query.listPets 字段列出所有宠物

  6. 通过 Query.listPetsByPriceRange 字段列出价格范围内的宠物

Mutation.createPet

在 AWS AppSync 控制台的架构编辑器中,在右侧为 createPet(input: CreatePetInput!): Pet 选择 Attach Resolver (附加解析程序)。选择您的 RDS 数据源。在 request mapping template (请求映射模板) 部分中,添加以下模板:

#set($id=$utils.autoId()) { "version": "2018-05-29", "statements": [ "insert into Pets VALUES ('$id', '$ctx.args.input.type', $ctx.args.input.price)", "select * from Pets WHERE id = '$id'" ] }

SQL 语句将根据语句数组中的顺序依次执行。结果将以相同的顺序返回。由于这是一个更改,因此,我们将在 insert 后运行 select 语句以检索提交的值,以便填充 GraphQL 响应映射模板。

response mapping template (响应映射模板) 部分中,添加以下模板:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])

由于语句有两个 SQL 查询,我们需要指定从具有 $utils.rds.toJsonString($ctx.result))[1][0]) 的数据库返回的矩阵中的第二个结果。

Mutation.updatePet

在 AWS AppSync 控制台的架构编辑器中,在右侧为 updatePet(input: UpdatePetInput!): Pet 选择 Attach Resolver (附加解析程序)。选择您的 RDS 数据源。在 request mapping template (请求映射模板) 部分中,添加以下模板:

{ "version": "2018-05-29", "statements": [ $util.toJson("update Pets set type='$ctx.args.input.type', price=$ctx.args.input.price WHERE id='$ctx.args.input.id'"), $util.toJson("select * from Pets WHERE id = '$ctx.args.input.id'") ] }

response mapping template (响应映射模板) 部分中,添加以下模板:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])

Mutation.deletePet

在 AWS AppSync 控制台的架构编辑器中,在右侧为 deletePet(input: DeletePetInput!): Pet 选择 Attach Resolver (附加解析程序)。选择您的 RDS 数据源。在 request mapping template (请求映射模板) 部分中,添加以下模板:

{ "version": "2018-05-29", "statements": [ $util.toJson("select * from Pets WHERE id='$ctx.args.input.id'"), $util.toJson("delete from Pets WHERE id='$ctx.args.input.id'") ] }

response mapping template (响应映射模板) 部分中,添加以下模板:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])

Query.getPet

现在,已为您的架构创建更改,我们将连接三个查询以展示如何获取各个项目和列表以及应用 SQL 筛选。在 AWS AppSync 控制台的架构编辑器中,在右侧为 getPet(id: ID!): Pet 选择 Attach Resolver (附加解析程序)。选择您的 RDS 数据源。在 request mapping template (请求映射模板) 部分中,添加以下模板:

{ "version": "2018-05-29", "statements": [ $util.toJson("select * from Pets WHERE id='$ctx.args.id'") ] }

response mapping template (响应映射模板) 部分中,添加以下模板:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])

Query.listPets

在 AWS AppSync 控制台的架构编辑器中,在右侧为 getPet(id: ID!): Pet 选择 Attach Resolver (附加解析程序)。选择您的 RDS 数据源。在 request mapping template (请求映射模板) 部分中,添加以下模板:

{ "version": "2018-05-29", "statements": [ "select * from Pets" ] }

response mapping template (响应映射模板) 部分中,添加以下模板:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])

Query.listPetsByPriceRange

在 AWS AppSync 控制台的架构编辑器中,在右侧为 getPet(id: ID!): Pet 选择 Attach Resolver (附加解析程序)。选择您的 RDS 数据源。在 request mapping template (请求映射模板) 部分中,添加以下模板:

{ "version": "2018-05-29", "statements": [ "select * from Pets where price > :MIN and price < :MAX" ], "variableMap": { ":MAX": $util.toJson($ctx.args.max), ":MIN": $util.toJson($ctx.args.min) } }

response mapping template (响应映射模板) 部分中,添加以下模板:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])

运行更改

现在,您已使用 SQL 语句配置所有解析程序并将 GraphQL API 连接到 Serverless Aurora 数据 API,可以开始执行更改和查询。在 AWS AppSync 控制台中,选择查询选项卡并输入以下内容来创建宠物:

mutation add { createPet(input : { type:fish, price:10.0 }){ id type price } }

该响应包含 id类型价格,如下所示:

{ "data": { "createPet": { "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a", "type": "fish", "price": "10.0" } } }

您可以通过运行 updatePet 更改来修改此项目:

mutation update { updatePet(input : { id:"c6fedbbe-57ad-4da3-860a-ffe8d039882a", type:bird, price:50.0 }){ id type price } }

请注意,我们使用了之前从 createPet 操作返回的 id。当解析程序利用 $util.autoId() 时,这将是您的记录的唯一值。您可以通过类似的方式删除记录:

mutation { deletePet(input : {id:ID_PLACEHOLDER}){ id type price } }

使用包含不同的价格 值的第一个更改创建几条记录,然后运行几个查询。

运行查询

同样,在控制台的查询选项卡中,使用以下语句列出您创建的所有记录:

query allpets { listPets { id type price } }

这很好,但我们可以借助以下 GraphQL 查询来利用将 where price > :MIN and price < :MAX 放入 Query.listPetsByPriceRange 的映射模板中的 SQL WHERE 谓词:

query { listPetsByPriceRange(minPrice:1, maxPrice:11) { id type price } }

您应仅看到包含高于 1 美元或低于 10 美元的价格 的记录。最后,您可以执行查询以检索各个记录,如下所示:

query { getPet(id:ID_PLACEHOLDER){ id type price } }

输入净化

我们强烈建议开发人员清理 GraphQL 操作的参数。执行此操作的一种方法是在对数据 API 执行 SQL 语句之前,在请求映射模板中提供特定于输入的验证步骤。让我们看看如何修改 listPetsByPriceRange 示例的请求映射模板。您可以执行以下操作,而不是仅依赖于用户输入:

#set($validMaxPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.maxPrice)) #set($validMinPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.minPrice)) #if (!$validMaxPrice || !$validMinPrice) $util.error("Provided price input is not valid.") #end { "version": "2018-05-29", "statements": [ "select * from Pets where price > :MIN and price < :MAX" ], "variableMap": { ":MAX": $util.toJson($ctx.args.maxPrice), ":MIN": $util.toJson($ctx.args.minPrice) } }

在对数据 API 执行解析程序时防止恶意输入的另一种方法是,将预编译语句与存储过程和参数化输入一起使用。例如,在 listPets 的解析程序中,定义以下将 select 作为预编译语句执行的过程:

CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END

可以使用以下 execute sql 命令在 Aurora Serverless 实例中创建此内容:

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:xxxxxxxxxxxx:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:xxxxxxxxxxxx:secret:httpendpoint-xxxxxx" \ --region us-east-1 --database "DB_NAME" \ --sql "CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END"

由于我们现在只是调用了存储过程,因此简化了为 listPets 生成的解析程序代码。至少,任何字符串输入都应该有单引号进行转义

#set ($validType = $util.isString($ctx.args.type) && !$util.isNullOrBlank($ctx.args.type)) #if (!$validType) $util.error("Input for 'type' is not valid.", "ValidationError") #end { "version": "2018-05-29", "statements": [ "CALL listPets(:type)" ] "variableMap": { ":type": $util.toJson($ctx.args.type.replace("'", "''")) } }

转义字符串

单引号表示 SQL 语句中字符串文字的开始和结束,例如,'some string value'。要允许在字符串中使用具有一个或多个单引号字符 ( ') 的字符串值,必须用两个单引号 ('') 替换每个字符串值。例如,如果输入字符串是 Nadia's dog,您可以针对 SQL 语句将其转义,例如

update Pets set type='Nadia''s dog' WHERE id='1'