Delen via


TripPin deel 2 - Gegevensconnector voor een REST-service

Deze meerdelige zelfstudie bevat informatie over het maken van een nieuwe gegevensbronextensie voor Power Query. De zelfstudie is bedoeld om opeenvolgend te worden uitgevoerd. Elke les bouwt voort op de connector die in de vorige lessen is gemaakt, en voegt incrementeel nieuwe mogelijkheden toe aan uw connector.

In deze les gaat u het volgende doen:

  • Een basisfunctie maken die aanroept naar een REST API met behulp van Web.Contents
  • Meer informatie over het instellen van aanvraagheaders en het verwerken van een JSON-antwoord
  • Power BI Desktop gebruiken om het antwoord te wrangleren in een gebruiksvriendelijke indeling

In deze les wordt de OData-connector voor de TripPin-service (gemaakt in de vorige les) geconverteerd naar een connector die lijkt op iets dat u zou maken voor een RESTful-API. OData is een RESTful-API, maar een met een vaste set conventies. Het voordeel van OData is dat het een schema, een protocol voor het ophalen van gegevens en een standaardquerytaal biedt. Als u het gebruik van OData.Feed wegneemt, moeten we deze mogelijkheden zelf inbouwen in de connector.

Samenvatting van de OData-connector

Voordat u de OData-functies van uw connector verwijdert, gaan we een beknopt overzicht maken van wat deze momenteel doet (meestal achter de schermen) om gegevens op te halen uit de service.

Open het TripPin-extensieproject vanuit deel 1 in Visual Studio Code. Open het querybestand en plak de volgende query:

TripPin.Feed("https://services.odata.org/v4/TripPinService/Me")

Open Fiddler en evalueer vervolgens het huidige Power Query-bestand in Visual Studio Code.

In Fiddler zijn er drie aanvragen voor de server:

Fiddler OData-aanvragen.

  • /Me— de werkelijke URL die u aanvraagt.
  • /$metadata— een aanroep die automatisch door de OData.Feed functie wordt gedaan om het schema te bepalen en informatie over het antwoord te typen.
  • /Me/BestFriend— een van de velden die (gretig) zijn opgehaald toen u de singleton /Me vermeldde. In dit geval heeft de aanroep een 204 No Content status opgeleverd.

M-evaluatie is meestal lui. In de meeste gevallen worden gegevenswaarden alleen opgehaald/opgehaald wanneer ze nodig zijn. Er zijn scenario's (zoals de case /Me/BestFriend) waarbij een waarde gretig wordt opgehaald. Dit gebeurt meestal wanneer typegegevens nodig zijn voor een lid en de engine heeft geen andere manier om het type te bepalen dan om de waarde op te halen en te inspecteren. Het maken van dingen lui (dat wil gezegd, het vermijden van gretige pulls) is een van de belangrijkste aspecten om een M-connector performant te maken.

Noteer de aanvraagheaders die samen met de aanvragen en de JSON-indeling van het antwoord van de aanvraag /Me zijn verzonden.

{
  "@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
}

Wanneer de query is geëvalueerd, moet in het resultaatvenster PQTest de recordwaarde voor de singleton Me worden weergegeven.

OData-resultaten.

Als u de velden in het uitvoervenster vergelijkt met de velden die worden geretourneerd in het onbewerkte JSON-antwoord, ziet u een onjuiste overeenkomst. Het queryresultaat bevat andere velden (Friends, Trips, GetFriendsTrips) die nergens in het JSON-antwoord worden weergegeven. De functie OData.Feed heeft deze velden automatisch aan de record toegevoegd op basis van het schema dat door $metadata wordt geretourneerd. Dit is een goed voorbeeld van hoe een connector het antwoord van de service kan uitbreiden en/of opnieuw kan opmaken om een betere gebruikerservaring te bieden.

Een eenvoudige REST-connector maken

U gaat nu een nieuwe geëxporteerde functie toevoegen aan uw connector die Web.Contents aanroept.

Als u geslaagde webaanvragen wilt kunnen indienen bij de OData-service, moet u echter enkele standaard OData-headers instellen. U doet dit door een algemene set headers te definiëren als een nieuwe variabele in uw connector:

DefaultRequestHeaders = [
    #"Accept" = "application/json;odata.metadata=minimal",  // column name and values only
    #"OData-MaxVersion" = "4.0"                             // we only support v4
];

U wijzigt de implementatie van uw TripPin.Feed functie zodat in plaats van het gebruik web.Contents wordt gebruikt OData.Feedom een webaanvraag uit te voeren en het resultaat als een JSON-document te parseren.

TripPinImpl = (url as text) =>
    let
        source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
        json = Json.Document(source)
    in
        json;

Vergeet niet om de connector te bouwen nu u wijzigingen hebt aangebracht in het connectorbestand. Vervolgens kunt u het querybestand evalueren (TripPin.query.pq). Het resultaat van de /Me-record lijkt nu op de onbewerkte JSON die u in de Fiddler-aanvraag hebt gezien.

Als u Fiddler bekijkt bij het uitvoeren van de nieuwe functie, ziet u ook dat de evaluatie nu één webaanvraag doet in plaats van drie. Gefeliciteerd: u hebt een prestatieverhoging van 300% bereikt. U bent nu alle type- en schemagegevens kwijt, maar u hoeft zich nog niet op dat onderdeel te concentreren.

Werk uw query bij om toegang te krijgen tot enkele TripPin-entiteiten/-tabellen, zoals:

  • https://services.odata.org/v4/TripPinService/Airlines
  • https://services.odata.org/v4/TripPinService/Airports
  • https://services.odata.org/v4/TripPinService/Me/Trips

U ziet dat de paden die worden gebruikt om mooi opgemaakte tabellen te retourneren, nu een waardeveld op het hoogste niveau retourneren met een ingesloten [lijst]. U moet enkele transformaties uitvoeren op het resultaat om het bruikbaar te maken voor scenario's voor eindgebruikersverbruik.

Lijst met resultaten.

Transformaties ontwerpen in Power Query

Hoewel het mogelijk is om uw M-transformaties handmatig te ontwerpen, geven de meeste mensen de voorkeur aan Power Query om hun gegevens vorm te geven. U opent uw extensie in Power BI Desktop en gebruikt deze om query's te ontwerpen om de uitvoer om te zetten in een gebruiksvriendelijkere indeling. Bouw uw oplossing opnieuw, kopieer het nieuwe extensiebestand naar de map Custom Data Verbinding maken ors en start Power BI Desktop opnieuw.

Start een nieuwe lege query en plak het volgende in de formulebalk:

= TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines")

Zorg ervoor dat u het =-teken opneemt.

Bewerk de uitvoer totdat deze eruitziet als de oorspronkelijke OData-feed: een tabel met twee kolommen: AirlineCode en Name.

Opgemaakte luchtvaartmaatschappijen.

De resulterende query ziet er ongeveer als volgt uit:

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

Geef de query een naam ('Airlines').

Maak een nieuwe lege query. Gebruik deze keer de TripPin.Feed functie voor toegang tot de entiteit /Airports. Pas transformaties toe totdat u iets ziet dat lijkt op de onderstaande share. De overeenkomende query vindt u ook hieronder. Geef deze query ook een naam ('Luchthavens').

Opgemaakte luchthavens.

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"

U kunt dit proces herhalen voor meer paden onder de service. Zodra u klaar bent, gaat u verder met de volgende stap van het maken van een (mock)-navigatietabel.

Een navigatietabel simuleren

Nu gaat u een tabel bouwen (met behulp van M-code) die uw mooi opgemaakte TripPin-entiteiten weergeeft.

Start een nieuwe lege query en open de Geavanceerde editor.

Plak de volgende query:

let
    source = #table({"Name", "Data"}, {
        { "Airlines", Airlines },
        { "Airports", Airports }
    })
in
    source

Als u de instelling Privacyniveaus niet hebt ingesteld op 'Instellingen voor privacyniveau altijd negeren' (ook wel 'Snel combineren' genoemd), ziet u een privacyprompt.

Firewall.

Privacyprompts worden weergegeven wanneer u gegevens uit meerdere bronnen combineert en nog geen privacyniveau hebt opgegeven voor een of meer bronnen. Selecteer de knop Doorgaan en stel het privacyniveau van de bovenste bron in op Openbaar.

Privacy.

Selecteer Opslaan en de tabel wordt weergegeven. Hoewel dit nog geen navigatietabel is, biedt deze de basisfunctionaliteit die u nodig hebt om deze in een volgende les om te zetten in een tabel.

FakeNav.

Gegevenscombinatiecontroles vinden niet plaats wanneer er vanuit een extensie toegang wordt verkregen tot meerdere gegevensbronnen. Omdat alle aanroepen van de gegevensbron vanuit de extensie dezelfde autorisatiecontext overnemen, wordt ervan uitgegaan dat ze 'veilig' zijn om te combineren. Uw extensie wordt altijd behandeld als één gegevensbron als het gaat om regels voor gegevenscombinaties. Gebruikers ontvangen nog steeds de reguliere privacyprompts bij het combineren van uw bron met andere M-bronnen.

Als u Fiddler uitvoert en de knop Voorbeeld vernieuwen selecteert in de Power Query-editor, noteert u de afzonderlijke webaanvragen voor elk item in de navigatietabel. Dit geeft aan dat er een gretige evaluatie plaatsvindt, wat niet ideaal is bij het bouwen van navigatietabellen met veel elementen. Volgende lessen laten zien hoe u een juiste navigatietabel bouwt die ondersteuning biedt voor luie evaluatie.

Conclusie

In deze les hebt u geleerd hoe u een eenvoudige connector voor een REST-service bouwt. In dit geval hebt u een bestaande OData-extensie omgezet in een standaard REST-extensie (met behulp van Web.Contents), maar dezelfde concepten zijn van toepassing als u een nieuwe extensie helemaal opnieuw maakt.

In de volgende les neemt u de query's die in deze les zijn gemaakt met behulp van Power BI Desktop en zet u deze om in een echte navigatietabel binnen de extensie.

Volgende stappen

TripPin deel 3 - Navigatietabellen