本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
将 Aurora Serverless v2 与 AWS AppSync
使用将您的 GraphQL API 连接到 Aurora 无服务器数据库。 AWS AppSync这种集成允许您通过 GraphQL 查询、突变和订阅来执行 SQL 语句,从而为您提供与关系数据交互的灵活方式。
注意
本教程使用 US-EAST-1
区域。
优势
GraphQL 和关系数据库之间的无缝集成
能够通过 GraphQL 接口执行 SQL 操作
使用 Aurora 无服务器 v2 实现无服务器可扩展性
通过 Secr AWS ets Manager 安全访问数据
通过输入清理来防止 SQL 注入
灵活的查询功能,包括筛选和范围操作
常见使用案例
根据关系数据要求构建可扩展的应用程序
APIs 既需要 GraphQL 灵活性又需要 SQL 数据库功能的创作
通过 GraphQL 突变和查询管理数据操作
实现安全的数据库访问模式
在本教程中,您将学习以下内容。
设置 Aurora 无服务器 v2 集群
启用数据 API 功能
创建和配置数据库结构
为数据库操作定义 GraphQL 架构
为查询和突变实现解析器
通过适当的输入清理来保护您的数据访问安全
通过 GraphQL 接口执行各种数据库操作
设置您的数据库集群
在向添加 Amazon RDS 数据源之前 AWS AppSync,必须先在 Aurora Serverless v2 集群上启用数据 API,然后使用配置密钥。AWS Secrets Manager您可以使用以下方法创建 Aurora Serverless v2 集群: AWS CLI
aws rds create-db-cluster \ --db-cluster-identifier appsync-tutorial \ --engine aurora-mysql \ --engine-version 8.0 \ --serverless-v2-scaling-configuration MinCapacity=0,MaxCapacity=1 \ --master-username USERNAME \ --master-user-password COMPLEX_PASSWORD \ --enable-http-endpoint
这将为集群返回一个 ARN。
创建集群后,必须使用以下命令添加 Aurora Serverless v2 实例。
aws rds create-db-instance \ --db-cluster-identifier appsync-tutorial \ --db-instance-identifier appsync-tutorial-instance-1 \ --db-instance-class db.serverless \ --engine aurora-mysql
注意
这些端点需要一段时间才能激活。您可以在 Amazon RDS 控制台的集群连接和安全选项卡中查看它们的状态。您也可以使用以下 AWS CLI 命令检查集群的状态。
aws rds describe-db-clusters \ --db-cluster-identifier appsync-tutorial \ --query "DBClusters[0].Status"
您可以使用 AWS Secrets Manager 控制台创建 S ecre t,也可以使用上 AWS CLI
一步中的USERNAME
和COMPLEX_PASSWORD
使用输入文件创建 Secret,如下所示。
{ "username": "USERNAME", "password": "COMPLEX_PASSWORD" }
将其作为参数传递给 AWS CLI:
aws secretsmanager create-secret --name HttpRDSSecret --secret-string file://creds.json --region us-east-1
这将为密钥返回 ARN。
记下您的 Aurora Serverless 集群的 ARN 和密钥,以便以后在创建数据源时在 AppSync 控制台中使用。
启用数据 API
您可以通过按照 RDS 文档中的说明操作来在您的集群上启用数据 API。在添加为数据源之前,必须启用 AppSync 数据 API。
创建数据库和表
在启用数据 API 后,您可以确保它在 AWS CLI中使用 aws
rds-data execute-statement
命令。这将确保您的 Aurora 无服务器集群在添加到 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 并导航到 Schema 页面,然后输入以下内容:
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 创建一个新角色,也可以使用类似于以下内容的策略创建一个角色:
请注意,此策略中有两个需要获得角色访问权的语句。第一个资源是您的 Aurora 无服务器集群,第二个资源是您的 AR AWS Secrets Manager N。在单击 “创建” 之前,您需要 ARNs 在 AppSync 数据源配置中同时提供两者。
将其作为参数传递给 AWS CLI。
aws secretsmanager create-secret \ --name HttpRDSSecret \ --secret-string file://creds.json \ --region us-east-1
这将为密钥返回 ARN。在控制台中创建数据源时,请记下您的 Aurora Serverless 集群的 ARN 和密钥,以备日后使用。 AWS AppSync
构建您的数据库结构
在启用数据 API 后,您可以确保它在 AWS CLI中使用 aws
rds-data execute-statement
命令。这将确保您的 Aurora Serverless v2 集群在添加到 API 之前已正确配置。 AWS AppSync 首先,使用如下--sql
参数创建一个名为 TESTDB 的数据库。
aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:
111122223333
:cluster:appsync-tutorial
" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333
:secret:appsync-tutorial-rds-secret
" \ --region us-east-1 \ --sql "create DATABASE TESTDB"
如果运行时没有错误,请使用以下 c reate table 命令添加表。
aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:
111122223333
:cluster:http-endpoint-test
" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333
:secret:testHttp2-AmNvc1
" \ --region us-east-1 \ --sql "create table Pets(id varchar(200), type varchar(200), price float)" \ --database "TESTDB"
设计你的 API 接口
在 Aurora Serverless v2 数据 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。对于该角色,您可以 AWS 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:
111122223333
:cluster:mydbcluster", "arn:aws:rds:us-east-1:111122223333
:cluster:mydbcluster:*" ] }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:111122223333
:secret:mysecret", "arn:aws:secretsmanager:us-east-1:111122223333
:secret:mysecret:*" ] } ] }
请注意,此策略中有两个需要获得角色访问权的语句。第一个资源是你的 Aurora Serverless v2 集群,第二个资源是你的 AR AWS Secrets Manager N。在单击 “创建” 之前,您需要 ARNs 在 AWS AppSync 数据源配置中同时提供两者。
将您的 API 连接到数据库操作
现在我们有了有效的 GraphQL 架构和 RDS 数据源,您可以将 GraphQL 字段的解析器附加到架构中。我们的 API 将提供以下功能:
-
使用 m utation.creat ePet 字段创建一只宠物
-
使用 m utation.updat ePet 字段更新宠物
-
使用 m utat ion.deletePet 字段删除宠物
-
通过 query.g etPet 字段获取单曲
-
使用 query. listPets 字段列出所有内容
-
使用查询列出价格区间内的宠物。 listPetsByPriceRange字段
Mutation.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 语句。结果将以相同的顺序返回。由于这是一种突变,因此您将在插入后运行 s elec t 语句来检索提交的值,以便填充 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
选择您的 RDS 数据源。在请求映射模板部分,添加以下模板。
{ "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) } }
在响应映射模板部分,添加以下模板。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
Mutation.deletePet
在 AWS AppSync 控制台的架构编辑器中,为其选择附加解析器。deletePet(input: DeletePetInput!):
Pet
选择您的 RDS 数据源。在请求映射模板部分,添加以下模板。
{ "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" } }
在响应映射模板部分,添加以下模板。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
Query.getPet
现在,已经为您的架构创建了突变,请连接三个查询,以展示如何获取单个项目、列表和应用 SQL 过滤。在 AWS AppSync 控制台的架构编辑器中,为其选择附加解析器。getPet(id:
ID!): Pet
选择您的 RDS 数据源。在请求映射模板部分,添加以下模板。
{ "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])
Query.listPets
在 AWS AppSync 控制台的架构编辑器中,在右侧选择 Att ach Resolver fo getPet(id: ID!):
Pet
r。选择您的 RDS 数据源。在请求映射模板部分,添加以下模板。
{ "version": "2018-05-29", "statements": [ "select * from Pets" ] }
在响应映射模板部分,添加以下模板。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
查询。 listPetsByPriceRange
在 AWS AppSync 控制台的架构编辑器中,在右侧选择 Att ach Resolver fo getPet(id: ID!):
Pet
r。选择您的 RDS 数据源。在请求映射模板部分,添加以下模板。
{ "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])
通过 API 修改您的数据
现在,您已使用 SQL 语句配置所有解析器并将 GraphQL API 连接到 Serverless 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
中的 SQL WH ER E 谓词进行查询。 listPetsByPriceRange使用以下 GraphQL 查询:
query petsByPriceRange { listPetsByPriceRange(min:1, max:11) { id type price } }
您应仅看到包含高于 1 美元或低于 10 美元的价格 的记录。最后,您可以执行查询以检索各个记录,如下所示:
query onePet { getPet(id:ID_PLACEHOLDER){ id type price } }
保护您的数据访问安全
SQL 注入是数据库应用程序中的一个安全漏洞。当攻击者通过用户输入字段插入恶意 SQL 代码时,就会发生这种情况。这可能允许未经授权访问数据库数据。我们建议您在使用之前仔细验证和清理所有用户输入,variableMap
以防止 SQL 注入攻击。如果不使用变量映射,则您有责任清理其 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
在你的 Aurora 无服务器 v2 实例中创建它。
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 语句中使用单引号来标记 SQL 语句中字符串字面的开头和结尾,例如 'some string value'
。要允许在字符串中使用具有一个或多个单引号字符 ( '
) 的字符串值,必须用两个单引号 (''
) 替换每个字符串值。例如,如果输入字符串是 Nadia's dog
,您可以针对 SQL 语句将其转义,例如
update Pets set type='Nadia''s dog' WHERE id='1'