本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
教學課程:將 Aurora 無伺服器搭配 AWS AppSync
AWS AppSync 提供資料來源,用SQL於對已透過資料啟用的 Amazon Aurora 無伺服器叢集執行命令。API您可以使用 AppSync 解析器對API具有 GraphQL 查詢、突變和訂閱的資料執行SQL陳述式。
建立叢集
將RDS資料來源新增至之前, AppSync 您必須先啟用 Aurora 無伺服器叢集API上的資料,並使用 AWS Secrets Manager. 您可以先使 AWS CLI用下列方式建立 Aurora 無伺服器叢集:
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集群的。
通過 AWS Secrets Manager 控制台或通過輸入文件(例如使用上一步的USERNAME和 COMPLEX _PASSWORD)創建密鑰:CLI
{ "username": "USERNAME", "password": "COMPLEX_PASSWORD" }
將此作為參數傳遞給 AWS CLI:
aws secretsmanager create-secret --name HttpRDSSecret --secret-string file://creds.json --region us-east-1
這將返回一ARN個秘密。
請記下 ARN Aurora 無伺服器叢集和密碼,以便稍後在建立資料來源時在 AppSync 主控台中使用。
啟用資料 API
您可以依照RDS文件中的指示啟用叢集API上的資料。在新增為資料來源之前,API必須先啟用「 AppSync 資料」。
建立資料庫及資料表
啟用資料之後,API您可以確保它可以與中的aws
rds-data execute-statement
指令搭配使用 AWS CLI。這將確保您的 Aurora 無伺服器叢集在將叢集新增到您的 AppSync API. 首先創建一個使用參數調TESTDB用的--sql
數據庫,如下所示:
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"
如果這次執行沒有錯誤,則搭配 create table 命令新增資料表:
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 無伺服器資料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 }
儲存您的結構描述並瀏覽到 Data Sources (資料來源) 頁面,並建立新的資料來源。選取資料來源類型為 Relational database (關聯式資料庫),並提供易記名稱。使用您在最後一個步驟中建立的資料庫名稱,以及您在其中建立資料庫的叢集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:*" ] } ] }
請注意,在您授予角色存取權的這個政策中有 2 個陳述式 。第一個資源是您的 Aurora 無伺服器叢集,第二個是您的 AWS Secrets Manager ARN. 您需要在 AppSync 資料來源組態BOTHARNs中提供,然後按一下 [建立]。
設定解析程式
現在,我們有一個有效的 GraphQL 模式和一個RDS數據源,我們可以將解析器附加到我們的架構上的 GraphQL 字段。我們API將提供以下功能:
-
通過突變創建一個寵物。 createPet字段
-
通過變異更新寵物。 updatePet字段
-
通過變異刪除寵物。 deletePet字段
-
通過查詢獲得一隻寵物。 getPet字段
-
通過查詢列出所有寵物。 listPets字段
-
通過查詢列出價格範圍內的寵物。 listPetsByPriceRange字段
突變。 createPet
在 AWS AppSync 主控台的資料架構編輯器中,選擇右側的「貼附解析器」。createPet(input:
CreatePetInput!): Pet
選擇 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語句將按順序執行,基於語句數組中的順序。結果將以相同順序傳回。由於這是一個突變,我們在插入後運行 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 主控台的資料架構編輯器中,選擇右側的「貼附解析器」。updatePet(input:
UpdatePetInput!): Pet
選擇 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 主控台的資料架構編輯器中,選擇右側的「貼附解析器」。deletePet(input:
DeletePetInput!): Pet
選擇 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 主控台的資料架構編輯器中,選擇右側的「貼附解析器」。getPet(id: ID!): Pet
選擇 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 主控台的資料架構編輯器中,選擇右側的「貼附解析器」。getPet(id: ID!):
Pet
選擇 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 主控台的資料架構編輯器中,選擇右側的「貼附解析器」。getPet(id: ID!):
Pet
選擇 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到無伺服器 Aurora 資料API,就可以開始執行突變和查詢。在 AWS AppSync 控制台中,選擇查詢選項卡,然後輸入以下內容以創建寵物:
mutation add { createPet(input : { type:fish, price:10.0 }){ id type price } }
回應應該包含如下的 id、type 和 price:
{ "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 } }
運用第一個變動,搭配 price 的幾個不同值,建立一些記錄,然後執行一些查詢。
執行查詢
繼續在主控台的 Queries (查詢) 標籤中,使用以下陳述式,列出您所建立的所有記錄:
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 } }
您應該只會看到 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
使用以下執行 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'