将 Aurora 无服务器与 AWS AppSync - AWS AppSync

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

将 Aurora 无服务器与 AWS AppSync

AWS AppSync 提供数据源,用于对已启用数据API的 Amazon Aurora 无服务器集群执行SQL命令。您可以使用 AppSync 解析器通过 GraphQL 查询、突变API和订阅对数据执行SQL语句。

创建集群

在向其中添加RDS数据源之前,必须先 AppSync 在 Aurora Serverless 集群API上启用数据,然后使用 AWS Secrets Manager配置密钥。您可以先通过以下方式创建 Aurora 无服务器集群: AWS CLI

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 _,通过 AWS Secrets Manager 控制台或使用输入文件创建 Secret,如下PASSWORD所示:CLI

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

将其作为参数传递给 AWS CLI:

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

这将返回 a fo ARN r secret。

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

启用数据 API

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

创建数据库和表

启用数据后,API您可以确保其与中的aws rds-data execute-statement命令配合使用 AWS CLI。这将确保在将 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 架构

现在,您API的 Aurora Serverless 数据已通过表启动并运行,我们将创建一个 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 无服务器集群,第二个资源是你 AWS Secrets Manager ARN的。在单击 “创建” 之前,您需要BOTHARNs在 AppSync 数据源配置中提供。

配置解析器

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

  1. 通过变异创造一只宠物。 createPet字段

  2. 通过变异更新宠物。 updatePet字段

  3. 通过变异删除一只宠物。 deletePet字段

  4. 通过查询获取一只宠物。 getPet字段

  5. 通过查询列出所有宠物。 listPets字段

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

突变。 createPet

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

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

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

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

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

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

突变。 updatePet

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

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

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

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

突变。 deletePet

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

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

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

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

查询。 getPet

现在,已经为您的架构创建了突变,我们将连接这三个查询,以展示如何获取单个项目、列表和应用SQL筛选。在 AWS AppSync 控制台的架构编辑器中,在右侧选择 Att ach Resolver fo getPet(id: ID!): Pet r。选择您的 RDS 数据源。在 request mapping template (请求映射模板) 部分中,添加以下模板:

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

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

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

查询。 listPets

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

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

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

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

查询。 listPetsByPriceRange

在 AWS AppSync 控制台的架构编辑器中,在右侧选择 Att ach Resolver fo getPet(id: ID!): Pet r。选择您的 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语句配置了所有解析器,并将您的 G API raphQL 连接到无服务器 Aurora API 数据,就可以开始执行变更和查询了。在 AWS AppSync 控制台中,选择 Quer ies 选项卡并输入以下内容来创建 Pet:

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: ID_PLACEHOLDER, type:bird, price:50.0 }){ id type price } }

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

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

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

运行查询

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

query allpets { listPets { id type price } }

这很不错,但让我们利用映射模板where price > :MIN and price < :MAX中的SQLWHERE谓词进行查询。 listPetsByPriceRange使用以下 GraphQL 查询:

query petsByPriceRange { listPetsByPriceRange(min:1, max:11) { id type price } }

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

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

输入净化

我们建议开发人员使用variableMap防御SQL注入攻击。如果不使用变量映射,则由开发人员负责清理其 GraphQL 操作的参数。一种方法是在针对您的数据执行SQL语句之前,在请求映射模板中提供输入特定的验证步骤API。让我们看看如何修改 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'