TripPin 第 2 部分 - REST 服務的數據連接器
此多部分教學課程涵蓋如何建立Power Query的新數據源延伸模組。 本教學課程旨在循序完成,每個課程都是以先前課程中建立的連接器為基礎,以累加方式將新功能新增至您的連接器。
在本課程中,您將會:
- 使用 Web.Contents 建立呼叫 REST API 的基底函式
- 瞭解如何設定要求標頭並處理 JSON 回應
- 使用 Power BI Desktop 將回應擷取成使用者易記格式
這一課會將 TripPin 服務的 OData 型連接器(在上一課中建立)轉換成類似您為任何 RESTful API 建立的連接器。 OData 是 RESTful API,但有一組固定的慣例。 OData 的優點是其提供架構、數據擷取通訊協議和標準查詢語言。 取下 OData.Feed 的使用需要我們自行在連接器中建置這些功能。
OData 連接器的回顧
在您從連接器移除 OData 函式之前,讓我們快速檢閱它目前執行的功能(大部分在幕後),以從服務擷取數據。
從 Visual Studio Code 中的第 1 部分開啟 TripPin 延伸模組專案。 開啟 [查詢] 檔案並貼上下列查詢:
TripPin.Feed("https://services.odata.org/v4/TripPinService/Me")
開啟 Fiddler,然後在 Visual Studio Code 中評估目前的 Power Query 檔案。
在 Fiddler 中,伺服器有三個要求:
/Me
—您要要求的實際 URL。/$metadata
—函式自動發出的OData.Feed
呼叫,以判斷回應的架構和類型資訊。/Me/BestFriend
—當您列出 /Me 單一時,會提取其中一個(急切地)的欄位。 在此情況下,呼叫會產生204 No Content
狀態。
M 評估大多是懶惰的。 在大部分情況下,數據值只會在需要時擷取/提取。 在某些情況下(例如 /Me/BestFriend 案例)會急切地提取值。 當成員需要型別資訊時,通常會發生這種情況,而且引擎沒有比擷取值並檢查它別別的方法。 讓事情懶惰(也就是避免急切的提取)是使 M 連接器高效能的關鍵層面之一。
請注意已連同要求和 /Me 要求回應的 JSON 格式一起傳送的要求標頭。
{
"@odata.context": "https://services.odata.org/v4/TripPinService/$metadata#Me",
"UserName": "aprilcline",
"FirstName": "April",
"LastName": "Cline",
"MiddleName": null,
"Gender": "Female",
"Age": null,
"Emails": [ "April@example.com", "April@contoso.com" ],
"FavoriteFeature": "Feature1",
"Features": [ ],
"AddressInfo": [
{
"Address": "P.O. Box 555",
"City": {
"Name": "Lander",
"CountryRegion": "United States",
"Region": "WY"
}
}
],
"HomeAddress": null
}
當查詢完成評估時,PQTest 結果視窗應該會顯示 Me singleton 的 [記錄] 值。
如果您比較輸出視窗中的欄位與原始 JSON 回應中傳回的欄位,您會注意到不相符。 查詢結果有其他欄位 (Friends
、Trips
GetFriendsTrips
) 不會出現在 JSON 回應中的任何位置。 OData.Feed 函式會根據$metadata傳回的架構,自動將這些欄位附加至記錄。 這是連接器如何增強和/或重新格式化服務的回應,以提供更佳用戶體驗的良好範例。
建立基本 REST 連接器
您現在會將新的導出函式新增至呼叫 Web.Contents 的連接器。
不過,若要能夠成功對 OData 服務提出 Web 要求,您必須設定一些 標準 OData 標頭。 您會在連接器中將一組常見的標頭定義為新的變數,以執行此動作:
DefaultRequestHeaders = [
#"Accept" = "application/json;odata.metadata=minimal", // column name and values only
#"OData-MaxVersion" = "4.0" // we only support v4
];
您可以變更函式的實作 TripPin.Feed
,而不是使用 OData.Feed
,它會使用 Web.Contents 提出 Web 要求,並將結果剖析為 JSON 檔。
TripPinImpl = (url as text) =>
let
source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
json = Json.Document(source)
in
json;
請記住,現在您已對連接器檔案進行變更,請記得建置連接器。 然後,您可以評估查詢檔案 (TripPin.query.pq)。 /Me 記錄的結果現在類似於您在 Fiddler 要求中看到的原始 JSON。
如果您在執行新函式時監看 Fiddler,您也會注意到評估現在會提出單一 Web 要求,而不是三個。 恭喜您—您已達到 300% 的效能提升! 您現在已遺失所有類型和架構資訊,但還不需要專注於該部分。
更新您的查詢以存取某些 TripPin 實體/資料表,例如:
https://services.odata.org/v4/TripPinService/Airlines
https://services.odata.org/v4/TripPinService/Airports
https://services.odata.org/v4/TripPinService/Me/Trips
您會發現,用來傳回良好格式數據表的路徑現在會傳回具有內嵌 [List] 的最上層 “value” 字段。 您必須對結果執行一些轉換,才能讓終端用戶取用案例使用。
在 Power Query 撰寫轉換
雖然您可以手動撰寫 M 轉換,但大多數人偏好使用 Power Query 來塑造其數據。 您將在 Power BI Desktop 中開啟延伸模組,並用它來設計查詢,將輸出轉換成更方便使用的格式。 重建您的解決方案、將新的延伸模組檔案複製到您的自定義數據 連線 ors 目錄,然後重新啟動 Power BI Desktop。
啟動新的空白查詢,並將下列內容貼到公式列中:
= TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines")
請務必包含 = 符號。
操作輸出,直到它看起來像原始的 OData 摘要—包含兩個數據行的數據表:AirlineCode 和 Name。
產生的查詢看起來應該像這樣:
let
Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines"),
value = Source[value],
toTable = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
expand = Table.ExpandRecordColumn(toTable, "Column1", {"AirlineCode", "Name"}, {"AirlineCode", "Name"})
in
expand
為查詢指定名稱 (“Airlines” )。
建立新的空白查詢。 這次,請使用 函 TripPin.Feed
式來存取 /Airports 實體。 套用轉換,直到您得到類似如下所示的共享為止。 您也可以在下方找到相符的查詢,也提供此查詢的名稱(“Airports”)。
let
Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airports"),
value = Source[value],
#"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"Name", "IcaoCode", "IataCode", "Location"}, {"Name", "IcaoCode", "IataCode", "Location"}),
#"Expanded Location" = Table.ExpandRecordColumn(#"Expanded Column1", "Location", {"Address", "Loc", "City"}, {"Address", "Loc", "City"}),
#"Expanded City" = Table.ExpandRecordColumn(#"Expanded Location", "City", {"Name", "CountryRegion", "Region"}, {"Name.1", "CountryRegion", "Region"}),
#"Renamed Columns" = Table.RenameColumns(#"Expanded City",{{"Name.1", "City"}}),
#"Expanded Loc" = Table.ExpandRecordColumn(#"Renamed Columns", "Loc", {"coordinates"}, {"coordinates"}),
#"Added Custom" = Table.AddColumn(#"Expanded Loc", "Latitude", each [coordinates]{1}),
#"Added Custom1" = Table.AddColumn(#"Added Custom", "Longitude", each [coordinates]{0}),
#"Removed Columns" = Table.RemoveColumns(#"Added Custom1",{"coordinates"}),
#"Changed Type" = Table.TransformColumnTypes(#"Removed Columns",{{"Name", type text}, {"IcaoCode", type text}, {"IataCode", type text}, {"Address", type text}, {"City", type text}, {"CountryRegion", type text}, {"Region", type text}, {"Latitude", type number}, {"Longitude", type number}})
in
#"Changed Type"
您可以在服務下針對更多路徑重複此程式。 準備好之後,請移至建立 (模擬) 瀏覽數據表的下一個步驟。
模擬瀏覽數據表
現在您要建置一個數據表(使用 M 程式代碼),以呈現您格式良好的 TripPin 實體。
啟動新的空白查詢,並顯示 進階編輯器。
貼上下列查詢:
let
source = #table({"Name", "Data"}, {
{ "Airlines", Airlines },
{ "Airports", Airports }
})
in
source
如果您尚未將 [隱私權等級] 設定設為 [一律忽略隱私權等級設定] (也稱為「快速合併」),您會看到隱私權提示。
當您結合來自多個來源的數據,但尚未指定一或多個來源的隱私權等級時,會出現隱私權提示。 選取 [ 繼續] 按鈕,並將最上層來源的隱私權層級設定為 [公用]。
選取 [ 儲存] ,您的數據表隨即出現。 雖然這還不是導覽數據表,但它提供您在後續課程中將其轉換成其中一項所需的基本功能。
從延伸模組記憶體取多個數據源時,不會進行數據組合檢查。 由於延伸模組內發出的所有數據源呼叫都會繼承相同的授權內容,因此假設它們是「安全」合併的。 在數據組合規則方面,您的延伸模組一律會被視為單一數據源。 將來源與其他 M 來源結合時,使用者仍會收到一般隱私權提示。
如果您執行 Fiddler 並選取 查詢編輯器 中的 [重新整理預覽] 按鈕,請記下導覽表格中每個項目的個別 Web 要求。 這表示正在進行積極評估,這在建置具有許多元素的導覽數據表時並不理想。 後續的課程會示範如何建置支援延遲評估的適當瀏覽數據表。
結論
本課程說明如何為 REST 服務建置簡單的連接器。 在此情況下,您已將現有的 OData 延伸模組轉換成標準 REST 延伸模組(使用 Web.Contents),但如果您從頭開始建立新的擴充功能,則適用相同的概念。
在下一課中,您將採用使用Power BI Desktop 在本課程中建立的查詢,並將其轉換成延伸模組內真正的導覽數據表。