PowerShell und Visual Studio Code in der Dataverse-Web-API verwenden

Dieser Artikel geht näher auf die Themen im Artikel Schnellstart-Web-API mit PowerShell ein und beschreibt erweiterte Funktionen, die mithilfe von PowerShell und Visual Studio Code mit der Dataverse-Web-API für Folgendes verwendet werden können:

Hinweis

Die Anweisungen in diesem Artikel sollten für Windows, Linux und macOS funktionieren. Die Schritte wurden jedoch nur mit Windows getestet. Wenn Änderungen erforderlich sind, teilen Sie uns dies über den Abschnitt Feedback am Ende dieses Artikels mit.

Anforderungen

Für den Inhalt dieses Artikels gelten dieselben Voraussetzungen wie für den Artikel Schnellstart-Web-API mit PowerShell.

Installieren Sie Folgendes oder überprüfen Sie, ob es vorhanden ist

Überprüfen der Installation

  1. Öffnen Sie Visual Studio Code.

  2. Wählen Sie im Terminal-Menü Neues Terminal aus.

  3. Wählen Sie im Visual Studio Code-Navigationsbereich das -Symbol für die PowerShell-Erweiterung aus.

  4. Kopieren und fügen Sie das folgende Skript im Terminalfenster von Visual Studio Code aus:

    Write-Host 'PowerShell Version:'$PSVersionTable.PSVersion.ToString()
    Write-Host 'PowerShell Az version:'(Get-InstalledModule Az).Version
    
  5. Drücken Sie die Eingabetaste. Die Ausgabe sollte wie folgt aussehen:

    PowerShell Version: 7.4.0
    PowerShell Az version: 11.1.0
    

Wenn Sie keine derartigen Ergebnisse sehen, installieren oder aktualisieren Sie die Voraussetzungen.

Darüber hinaus brauchen Sie

  • Ein gültiges Benutzerkonto für eine Dataverse-Umgebung
  • Die URL zur Dataverse-Umgebung, mit der Sie eine Verbindung herstellen möchten. Unter Entwicklerressourcen anzeigen erfahren Sie, wie Sie ihn finden. Sie sieht ungefähr so aus: https://yourorg.crm.dynamics.com/, wobei yourorg.crm anders ist.
  • Grundlegende Kenntnisse über die PowerShell-Skriptsprache

Wiederverwendbare Funktionen erstellen

Schnellstart Web-API mit PowerShell bot eine Einführung in die Authentifizierung und den Aufruf der WhoAmI-Funktion mit Visual Studio Code. Für einen Ad-hoc-Test eines oder mehrerer Vorgänge ist dieser Ansatz möglicherweise ausreichend. Wenn Ihre Skripte jedoch komplexer werden, müssen Sie möglicherweise immer wieder denselben Code eingeben.

In diesem Abschnitt beginnen wir mit der Erstellung einer Reihe wiederverwendbarer Funktionen in separaten Dateien, auf die wir über Dot-Sourcing zugreifen können. Verwenden Sie Dot-Sourcing, um eine Datei mit PowerShell-Skripten zu laden, die Funktionen und Variablen enthalten können, die Teil des lokalen Skriptbereichs werden.

Tipp

Vollständig dokumentierte Definitionen dieser Funktionen und mehr finden Sie in unserem PowerApps-Beispiel-Repository auf GitHub unter PowerApps-Samples/dataverse/webapi/PS/

Eine Verbindungsfunktion erstellen

Fügen wir den Code zur Authentifizierung in Dataverse in eine Funktion namens Connect in einer Datei namens Core.ps1 ein, damit wir ihn in einer einzigen Codezeile wiederverwenden können.

  1. Erstellen Sie einen Ordner. In diesem Beispiel erstellen wir einen Ordner in C:\scripts.

  2. Öffnen Sie den Skriptordner innerhalb von Visual Studio Code.

  3. Erstellen Sie im Skriptordner eine Textdatei mit dem Namen Core.ps1.

  4. Kopieren Sie die folgende Connect-Funktion und fügen Sie sie dann in die Core.ps1-Datei ein.

    function Connect {
       param (
          [Parameter(Mandatory)] 
          [String] 
          $uri
       )
    
       ## Login interactively if not already logged in
       if ($null -eq (Get-AzTenant -ErrorAction SilentlyContinue)) {
          Connect-AzAccount | Out-Null
       }
    
       # Get an access token
       $token = (Get-AzAccessToken -ResourceUrl $uri).Token
    
       # Define common set of headers
       $global:baseHeaders = @{
          'Authorization'    = 'Bearer ' + $token
          'Accept'           = 'application/json'
          'OData-MaxVersion' = '4.0'
          'OData-Version'    = '4.0'
       }
    
       # Set baseURI
       $global:baseURI = $uri + 'api/data/v9.2/'
    }
    

    Hinweis

    Das Skript fügt die baseURI- und baseHeaders-Variablen mithilfe des $global Bereichsmodifizierer zum globalen Kontext hinzu, sodass sie für andere Skripte in derselben Sitzung verfügbar sind.

  5. Erstellen Sie eine weitere Textdatei im Visual Studio Codenamen test.ps1 in Ihrem scripts Ordner.

  6. Kopieren Sie das folgende Skript in die test.ps1-Datei:

    . $PSScriptRoot\Core.ps1
    
    Connect 'https://yourorg.crm.dynamics.com/' # change to your organization
    # Invoke WhoAmI Function
    Invoke-RestMethod -Uri ($baseURI + 'WhoAmI') -Method Get -Headers $baseHeaders
    | ConvertTo-Json
    

    . $PSScriptRoot\Core.ps1 oben in der Datei verwendet Dot Sourcing , um das Skript anzuweisen, den Inhalt dieser Datei zu laden.

    Denken Sie daran, https://yourorg.crm.dynamics.com/ so zu ändern, dass es der URL für Ihr Umgebung entspricht.

  7. Um das Skript auszuführen, drücken Sie F5.

    Die Ausgabe könnte ungefähr wie diese aussehen:

    PS C:\scripts> . 'C:\scripts\test.ps1'
    {
    "@odata.context": "https://yourorg.crm.dynamics.com/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.WhoAmIResponse",
    "BusinessUnitId": "3a277578-5996-ee11-be36-002248227994",
    "UserId": "2c2e7578-5996-ee11-be36-002248227994",
    "OrganizationId": "97bf0e8b-aa99-ee11-be32-000d3a106c3a"
    }
    

Eine WhoAmI-Funktion erstellen

Fügen wir den Code zum Aufrufen der WhoAmI-Funktion in eine Funktion namens Get-WhoAmI in einer Datei mit dem Namen CommonFunctions.ps1 ein, damit wir jedes Mal, wenn wir die WhoAmI-Funktion verwenden möchten, nur elf und nicht 100 Zeichen eingeben müssen

  1. Erstellen Sie in Ihrem scripts-Ordner eine neue Textdatei namens CommonFunctions.ps1.

  2. Kopieren Sie die folgende Funktionsdefinition und fügen Sie sie in die CommonFunctions.ps1 ein.

    function Get-WhoAmI{
    
       $WhoAmIRequest = @{
          Uri = $baseURI + 'WhoAmI'
          Method = 'Get'
          Headers = $baseHeaders
       }
    
       Invoke-RestMethod @WhoAmIRequest
    }
    

    Hinweis

    Diese Funktionsdefinition verwendet eine Technik namens Splatting. Durch Splatting werden Ihre Befehle kürzer und leichter lesbar, da eine Sammlung von Parameterwerten als Einheit an einen Befehl übergeben wird.

  3. Speichern Sie die Datei CommonFunctions.ps1.

  4. Ändern Sie die Datei test.ps1 so, dass sie wie das folgende Skript aussieht:

    . $PSScriptRoot\Core.ps1
    . $PSScriptRoot\CommonFunctions.ps1
    
    Connect 'https://yourorg.crm.dynamics.com/' # change to your organization
    # Invoke WhoAmI Function
    Get-WhoAmI | ConvertTo-Json
    

    Denken Sie daran, den https://yourorg.crm.dynamics.com/-Wert so zu ändern, dass er mit der URL für Ihre Umgebung übereinstimmt.

  5. Um das Skript auszuführen, drücken Sie F5.

    Die Ausgabe sollte genauso aussehen wie zuvor.

Tabellenvorgangsfunktionen erstellen

Legen wir Funktionen zum Ausführen allgemeiner Tabellenvorgänge in eine Datei mit dem Namen TableOperations.ps1 fest, damit wir sie wiederverwenden können.

  1. Erstellen Sie in Ihrem scripts-Ordner eine neue Textdatei namens TableOperations.ps1.

  2. Kopieren Sie die folgende Funktionsdefinitionen und fügen Sie sie in die TableOperations.ps1 ein.

    function Get-Records {
       param (
          [Parameter(Mandatory)] 
          [String] 
          $setName,
          [Parameter(Mandatory)] 
          [String] 
          $query
       )
       $uri = $baseURI + $setName + $query
       # Header for GET operations that have annotations
       $getHeaders = $baseHeaders.Clone()
       $getHeaders.Add('If-None-Match', $null)
       $getHeaders.Add('Prefer', 'odata.include-annotations="*"')
       $RetrieveMultipleRequest = @{
          Uri     = $uri
          Method  = 'Get'
          Headers = $getHeaders
       }
       Invoke-RestMethod @RetrieveMultipleRequest
    }
    
    function New-Record {
       param (
          [Parameter(Mandatory)] 
          [String] 
          $setName,
          [Parameter(Mandatory)] 
          [hashtable]
          $body
       )
       $postHeaders = $baseHeaders.Clone()
       $postHeaders.Add('Content-Type', 'application/json')
    
       $CreateRequest = @{
          Uri     = $baseURI + $setName
          Method  = 'Post'
          Headers = $postHeaders
          Body    = ConvertTo-Json $body
       }
       Invoke-RestMethod @CreateRequest -ResponseHeadersVariable rh | Out-Null
       $url = $rh['OData-EntityId']
       $selectedString = Select-String -InputObject $url -Pattern '(?<=\().*?(?=\))'
       return [System.Guid]::New($selectedString.Matches.Value.ToString())
    }
    
    function Get-Record {
       param (
          [Parameter(Mandatory)] 
          [String] 
          $setName,
          [Parameter(Mandatory)] 
          [Guid] 
          $id,
          [String] 
          $query
       )
       $uri = $baseURI + $setName
       $uri = $uri + '(' + $id.Guid + ')' + $query
       $getHeaders = $baseHeaders.Clone()
       $getHeaders.Add('If-None-Match', $null)
       $getHeaders.Add('Prefer', 'odata.include-annotations="*"')
       $RetrieveRequest = @{
          Uri     = $uri
          Method  = 'Get'
          Headers = $getHeaders
       }
       Invoke-RestMethod @RetrieveRequest
    }
    
    function Update-Record {
       param (
          [Parameter(Mandatory)] 
          [String] 
          $setName,
          [Parameter(Mandatory)] 
          [Guid] 
          $id,
          [Parameter(Mandatory)] 
          [hashtable]
          $body
       )
       $uri = $baseURI + $setName
       $uri = $uri + '(' + $id.Guid + ')'
       # Header for Update operations
       $updateHeaders = $baseHeaders.Clone()
       $updateHeaders.Add('Content-Type', 'application/json')
       $updateHeaders.Add('If-Match', '*') # Prevent Create
       $UpdateRequest = @{
          Uri     = $uri
          Method  = 'Patch'
          Headers = $updateHeaders
          Body    = ConvertTo-Json $body
       }
       Invoke-RestMethod @UpdateRequest
    }
    
    function Remove-Record {
       param (
          [Parameter(Mandatory)] 
          [String]
          $setName,
          [Parameter(Mandatory)] 
          [Guid] 
          $id
       )
       $uri = $baseURI + $setName
       $uri = $uri + '(' + $id.Guid + ')'
       $DeleteRequest = @{
          Uri     = $uri
          Method  = 'Delete'
          Headers = $baseHeaders
       }
       Invoke-RestMethod @DeleteRequest
    }
    
    

    Informationen zum Verfassen dieser Anforderungen finden Sie in den folgenden Artikeln:

  3. Speichern Sie die Datei TableOperations.ps1.

  4. Kopieren Sie den folgenden Code und fügen Sie ihn dann in die test.ps1-Datei ein.

    . $PSScriptRoot\Core.ps1
    . $PSScriptRoot\CommonFunctions.ps1
    . $PSScriptRoot\TableOperations.ps1
    
    Connect 'https://yourorg.crm.dynamics.com/' # change to your organization
    
    # Retrieve Records
    Write-Host 'Retrieve first three account records:'
    (Get-Records `
       -setName accounts `
       -query '?$select=name&$top=3').value | 
    Format-Table -Property name, accountid
    
    # Create a record
    Write-Host 'Create an account record:'
    $newAccountID = New-Record `
       -setName accounts `
       -body @{
          name                = 'Example Account'; 
          accountcategorycode = 1 # Preferred
       }
    Write-Host "Account with ID $newAccountID created"
    
    # Retrieve a record
    Write-Host 'Retrieve the created record:'
    Get-Record `
       -setName  accounts `
       -id $newAccountID.Guid '?$select=name,accountcategorycode' |
    Format-List -Property name,
    accountid,
    accountcategorycode,
    accountcategorycode@OData.Community.Display.V1.FormattedValue
    
    # Update a record
    Write-Host 'Update the record:'
    $updateAccountData = @{
       name                = 'Updated Example account';
       accountcategorycode = 2; #Standard
    }
    Update-Record `
       -setName accounts `
       -id $newAccountID.Guid `
       -body $updateAccountData
    Write-Host 'Retrieve the updated the record:'
    Get-Record `
       -setName accounts `
       -id  $newAccountID.Guid `
       -query '?$select=name,accountcategorycode' |
    Format-List -Property name,
    accountid,
    accountcategorycode,
    accountcategorycode@OData.Community.Display.V1.FormattedValue
    
    # Delete a record
    Write-Host 'Delete the record:'
    Remove-Record `
       -setName accounts `
       -id $newAccountID.Guid
    Write-Host "The account with ID $newAccountID was deleted"
    

    Denken Sie daran, den https://yourorg.crm.dynamics.com/-Wert so zu ändern, dass er mit der URL für Ihre Umgebung übereinstimmt.

  5. Um das Skript auszuführen, drücken Sie F5.

    Die Ausgabe könnte ungefähr wie diese aussehen:

    PS C:\scripts> . 'C:\scripts\test.ps1'
    Retrieve first three account records:
    
    name                     accountid
    ----                     ---------
    Fourth Coffee (sample)   d2382248-cd99-ee11-be37-000d3a9b7981
    Litware, Inc. (sample)   d4382248-cd99-ee11-be37-000d3a9b7981
    Adventure Works (sample) d6382248-cd99-ee11-be37-000d3a9b7981
    
    Create an account record:
    Account with ID  a2c3ebc2-39a8-ee11-be37-000d3a8e8e07 created
    Retrieve the created record:
    
    name                                                          : Example Account
    accountid                                                     : a2c3ebc2-39a8-ee11-be37-000d3a8e8e07
    accountcategorycode                                           : 1
    accountcategorycode@OData.Community.Display.V1.FormattedValue : Preferred Customer
    
    Update the record:
    
    Retrieve the updated the record:
    
    name                                                          : Updated Example account
    accountid                                                     : a2c3ebc2-39a8-ee11-be37-000d3a8e8e07
    accountcategorycode                                           : 2
    accountcategorycode@OData.Community.Display.V1.FormattedValue : Standard
    
    Delete the record:
    
    The account with ID  a2c3ebc2-39a8-ee11-be37-000d3a8e8e07 was deleted
    

Verarbeiten von Ausnahmen

Bisher haben Sie in diesem Artikel den für Sie bereitgestellten Code kopiert und eingefügt. Wenn Sie jedoch beginnen, Ihre eigenen Funktionen zu schreiben und zu verwenden, können Fehler auftreten. Wenn diese Fehler auftreten, können sie von Dataverse oder Ihrem Skript herrühren.

Fügen Sie eine Hilfsfunktion hinzu, die beim Erkennen der Fehlerquelle helfen und relevante Details aus den von Dataverse zurückgegebenen Fehlern extrahieren kann.

  1. Fügen Sie der Invoke-DataverseCommands Datei die folgende Core.ps1 Funktion hinzu:

    function Invoke-DataverseCommands {
       param (
          [Parameter(Mandatory)] 
          $commands
       )
       try {
          Invoke-Command $commands
       }
       catch [Microsoft.PowerShell.Commands.HttpResponseException] {
          Write-Host "An error occurred calling Dataverse:" -ForegroundColor Red
          $statuscode = [int]$_.Exception.StatusCode;
          $statusText = $_.Exception.StatusCode
          Write-Host "StatusCode: $statuscode ($statusText)"
          # Replaces escaped characters in the JSON
          [Regex]::Replace($_.ErrorDetails.Message, "\\[Uu]([0-9A-Fa-f]{4})", 
             {[char]::ToString([Convert]::ToInt32($args[0].Groups[1].Value, 16))} )
    
       }
       catch {
          Write-Host "An error occurred in the script:" -ForegroundColor Red
          $_
       }
    }
    

    Die Invoke-DataverseCommands-Funktion verwendet das Invoke-Command-Cmdlet, um eine Reihe von Befehlen innerhalb eines try/catch-Blocks zu verarbeiten. Alle von Dataverse zurückgegebenen Fehler sind HttpResponseException-Fehler, daher schreibt der erste catch-Block eine An error occurred calling Dataverse:-Nachricht mit den JSON-Fehlerdaten an das Terminal.

    Die JSON-Daten in $_.ErrorDetails.Message enthalten einige maskierte Unicode-Zeichen. Zum Beispiel: \u0026 statt & und \u0027 statt '. Diese Funktion enthält Code, der diese Zeichen durch unmaskierte Zeichen ersetzt, sodass sie genau mit Fehlern übereinstimmen, die Sie an anderer Stelle sehen.

    Andernfalls werden die Fehler in das Terminalfenster zurückgeschrieben und zwar mit einer Meldung An error occurred in the script:

  2. Speichern Sie die Datei Core.ps1.

  3. Bearbeiten Sie die test.ps1 Datei, um das folgende Skript hinzuzufügen, das einen ungültigen setName Parameterwert verwendet. Der account Parameter sollte accounts sein. Dieser Fehler tritt häufig auf.

    . $PSScriptRoot\Core.ps1
    . $PSScriptRoot\CommonFunctions.ps1
    . $PSScriptRoot\TableOperations.ps1
    
    Connect 'https://yourorg.crm.dynamics.com/' # change this
    
    Invoke-DataverseCommands {
    
       # Retrieve Records
       Write-Host 'Retrieve first three account records:'
          (Get-Records `
          -setName account `
          -query '?$select=name&$top=3').value | 
       Format-Table -Property name, accountid
    
    }
    

    Denken Sie daran, den https://yourorg.crm.dynamics.com/-Wert so zu ändern, dass er mit der URL für Ihre Umgebung übereinstimmt.

  4. Um das Skript auszuführen, drücken Sie F5.

    Die Ausgabe könnte ungefähr wie diese aussehen:

    PS C:\scripts> . 'C:\scripts\test.ps1'
    Retrieve first three account records:
    An error occurred calling Dataverse:
    StatusCode: 404 (NotFound)
    
    {
    "error": {
       "code": "0x80060888",
       "message": "Resource not found for the segment 'account'."
       }
    }
    
  5. Bearbeiten Sie die test.ps1-Datei, um einen Skriptfehler innerhalb des Invoke-DataverseCommands-Blocks auszulösen:

    Invoke-DataverseCommands {
    
       throw 'A script error'
    
    }
    
  6. Um das Skript auszuführen, drücken Sie F5.

    Die Ausgabe sollte fast die gleiche sein, als wäre sie nicht im Invoke-DataverseCommands-Block enthalten:

    PS C:\scripts> . 'C:\scripts\test.ps1'
    An error occurred in the script:
    Exception: C:\scripts\test.ps1:8:4
    Line |
       8 |     throw 'A script error'
         |     ~~~~~~~~~~~~~~~~~~~~~~
         | A script error
    

Dataverse-Dienstschutzgrenzwerte verwalten

Die Dataverse-Dienstschutz-API-Grenzwerte helfen dabei, sicherzustellen, dass Dataverse für konsistente Verfügbarkeit und Leistung sorgt. Wenn Client-Anwendungen unter Verwendung der Web-API außerordentliche Anforderungen an die Serverressourcen stellen, Dataverse wird der Fehler 429 Too Many Requests zurückgegeben, und Client-Anwendungen müssen ihre Vorgänge für die im Retry-After-Header angegebene Dauer anhalten.

Der PowerShell MaximumRetryCount-Parameter des Invoke-RestMethod-Cmdlet gibt an, wie oft PowerShell eine Anforderung wiederholt, wenn ein Fehlercode zwischen 400 und einschließlich 599 oder 304 eingeht. Dies bedeutet, dass PowerShell Dataverse-Dienstschutz-429-Fehler erneut versucht, wenn Sie einen Wert für diesen Parameter angeben. Der MaximumRetryCount-Parameter kann mit RetryIntervalSec verwendet werden, um die Anzahl der zu wartenden Sekunden anzugeben. Der Standardwert sind fünf Sekunden. Wenn die Fehlerantwort einen Retry-After-Header für einen 429-Fehler enthält, was bei Dataverse-Dienstschutzfehlern der Fall ist, wird stattdessen dieser Wert verwendet.

Es kann sein, dass nie ein Dienstschutzgrenzwertfehler auftritt, während Sie sich mit der Dataverse-Web-API mit PowerShell vertraut machen. Die von Ihnen geschriebenen Skripts senden jedoch möglicherweise eine große Anzahl von Anfragen, die Fehler verursachen. Informieren Sie sich daher, wie Sie diese am besten mit PowerShell verwalten können.

Wenn Sie den MaximumRetryCount-Parameter zu jedem Dataverse-Aufruf mit Invoke-RestMethod hinzufügen, versucht PowerShell eine Vielzahl von Fehlern erneut. Wenn Sie jeden Fehler erneut versuchen, werden Ihre Skripte langsamer, insbesondere beim Entwickeln und Testen. Bei jedem Fehler müssten Sie 10 bis 15 Sekunden warten, je nachdem, wie viele Wiederholungen Sie angeben. Ein alternativer Ansatz besteht darin, Invoke-RestMethod in Ihre eigene Methode einzuschließen, die Wiederholungsversuche für bestimmte Fehler verwaltet.

Die folgende Invoke-ResilientRestMethod Funktion verwendet ein request Hashtabellenobjekt als obligatorischen Parameter und ein boolesch returnHeader Flag, um anzugeben, ob der Header Antwort zurückgegeben werden soll oder nicht. Wenn $returnHeader true ist, wird die Anforderung mithilfe des Invoke-RestMethod Befehls mit dem Parameter ResponseHeadersVariable gesendet, um die zurückgegebenen Header zu erfassen. Die Funktion verwendet Out-Null , sodass die Ausgabe, die den leeren Antwort-Textkörper darstellt, nicht mit der Funktion zurückgegeben wird. Andernfalls sendet die Funktion die Anforderung mit dem Invoke-RestMethod Objekt und gibt den Body Antwort zurück. request

Wenn Invoke-RestMethod mit einem 429-Fehler fehlschlägt, wird geprüft, ob das request-Objekt eine MaximumRetryCount-Eigenschaft hat. Wenn die Funktion erfolgreich ist, erstellt sie eine MaximumRetryCount Eigenschaft, die auf 3 gesetzt ist. Der Invoke-RestMethod Aufruf wird dann mit dem Anforderungsobjekt und dem Header-Wert Retry-After Antwort wiederholt. Wenn die returnHeader-Kennzeichnung wahr ist, wird der Antwortheader zurückgegeben. Wenn Invoke-RestMethod mit einem anderen Fehler fehlschlägt, wird die Ausnahme erneut ausgelöst.

function Invoke-ResilientRestMethod {
   param (
      [Parameter(Mandatory)] 
      $request,
      [bool]
      $returnHeader
   )
   try {
      if ($returnHeader) {
         Invoke-RestMethod @request -ResponseHeadersVariable rhv | Out-Null
         return $rhv
      }
      Invoke-RestMethod @request
   }
   catch [Microsoft.PowerShell.Commands.HttpResponseException] {
      $statuscode = $_.Exception.Response.StatusCode
      # 429 errors only
      if ($statuscode -eq 'TooManyRequests') {
         if (!$request.ContainsKey('MaximumRetryCount')) {
            $request.Add('MaximumRetryCount', 3)
            # Don't need - RetryIntervalSec
            # When the failure code is 429 and the response includes the Retry-After property in its headers, 
            # the cmdlet uses that value for the retry interval, even if RetryIntervalSec is specified
         }
         # Will attempt retry up to 3 times
         if ($returnHeader) {
            Invoke-RestMethod @request -ResponseHeadersVariable rhv | Out-Null
            return $rhv
         }
         Invoke-RestMethod @request
      }
      else {
         throw $_
      }
   }
   catch {
      throw $_
   }
}

Sie können eine ähnliche Funktion in Ihren wiederverwendbaren Funktionen verwenden. Wenn Funktionen Werte aus dem Header der Antwort zurückgeben müssen, müssen sie den returnHeader-Wert auf $true setzen. Beispielsweise ändert die folgende New-Record-Funktion die Beispielfunktion in Tabellenvorgangsfunktionen erstellen so, dass sie Invoke-ResilientRestMethod anstelle von direkt Invoke-RestMethod verwendet.

function New-Record {
   param (
      [Parameter(Mandatory)] 
      [String] 
      $setName,
      [Parameter(Mandatory)] 
      [hashtable]
      $body
   )
   $postHeaders = $baseHeaders.Clone()
   $postHeaders.Add('Content-Type', 'application/json')
   
   $CreateRequest = @{
      Uri     = $environmentUrl + 'api/data/v9.2/' + $setName
      Method  = 'Post'
      Headers = $postHeaders
      Body    = ConvertTo-Json $body

   }
   # Before: 
   # Invoke-RestMethod @CreateRequest -ResponseHeadersVariable rh | Out-Null

   # After:
   $rh = Invoke-ResilientRestMethod -request $CreateRequest -returnHeader $true
   $url = $rh['OData-EntityId']
   $selectedString = Select-String -InputObject $url -Pattern '(?<=\().*?(?=\))'
   return [System.Guid]::New($selectedString.Matches.Value.ToString())
}

Andernfalls kann die Invoke-ResilientRestMethod die Invoke-RestMethod wie in diesem Get-Record-Beispiel gezeigt ersetzen:

function Get-Record {
   param (
      [Parameter(Mandatory)] 
      [String] 
      $setName,
      [Parameter(Mandatory)] 
      [Guid] 
      $id,
      [String] 
      $query
   )
   $uri = $environmentUrl + 'api/data/v9.2/' + $setName
   $uri = $uri + '(' + $id.Guid + ')' + $query
   $getHeaders = $baseHeaders.Clone()
   $getHeaders.Add('If-None-Match', $null)
   $getHeaders.Add('Prefer', 'odata.include-annotations="*"')
   $RetrieveRequest = @{
      Uri     = $uri
      Method  = 'Get'
      Headers = $getHeaders
   }
   # Before:
   # Invoke-RestMethod @RetrieveRequest

   # After: 
   Invoke-ResilientRestMethod $RetrieveRequest
}

Der einzige Unterschied besteht darin, dass Sie die Hashtabelle ($RetrieveRequest) an die Methode übergeben, anstatt Splatting (@RetrieveRequest) zu verwenden. Andernfalls erhalten Sie einen Skriptfehler: A parameter cannot be found that matches parameter name 'Headers'.

Debuggen mithilfe von Fiddler

Fiddler ist ein Web-Debugging-Proxy, der zum Anzeigen des HTTP-Datenverkehrs auf Ihrem Computer verwendet wird. Das Anzeigen dieser Daten ist beim Debuggen von Skripten hilfreich. Standardmäßig sind HTTP-Anfragen und -Antworten, die mit dem Cmdlet „Invoke-RestMethod“ gesendet werden, nicht sichtbar, wenn Sie Fiddler verwenden.

Um HTTP-Verkehr in Fiddler anzuzeigen, legen Sie den Invoke-RestMethod Proxy-Parameter auf die URL fest, die auf Ihrem lokalen Computer als Fiddler-Proxy konfiguriert ist. Die URL lautet standardmäßig http://127.0.0.1:8888. Ihre URL kann anders sein.

Wenn Sie beispielsweise die WhoAmI-Funktion mit dem -Proxy-Parameter aufrufen, während Fiddler Datenverkehr erfasst:

Invoke-RestMethod `
   -Uri ($environmentUrl + 'api/data/v9.2/WhoAmI') `
   -Method Get `
   -Headers $baseHeaders `
   -Proxy 'http://127.0.0.1:8888'

In Fiddler können Sie alle Details sehen:

GET https://yourorg.api.crm.dynamics.com/api/data/v9.2/WhoAmI HTTP/1.1
Host: yourorg.api.crm.dynamics.com
OData-MaxVersion: 4.0
Accept: application/json
Authorization: Bearer [REDACTED]
OData-Version: 4.0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.22631; en-US) PowerShell/7.4.0
Accept-Encoding: gzip, deflate, br


HTTP/1.1 200 OK
Cache-Control: no-cache
Allow: OPTIONS,GET,HEAD,POST
Content-Type: application/json; odata.metadata=minimal
Expires: -1
Vary: Accept-Encoding
x-ms-service-request-id: 7341c0c1-3343-430b-98ea-292567ed4776
Set-Cookie: ARRAffinity=f60cbee43b7af0a5f322e7ce57a018546ed978f67f0c11cbb5e15b02ddb091a915134d20c556b0b34b9b6ae43ec3f5dcdad61788de889ffc592af7aca85fc1c508DC0FC94CB062A12107345846; path=/; secure; HttpOnly
Set-Cookie: ReqClientId=4fc95009-0b3d-4a19-b223-0d80745636ac; expires=Sun, 07-Jan-2074 21:10:42 GMT; path=/; secure; HttpOnly
Set-Cookie: orgId=648e8efd-db86-466e-a5bc-a4d5eb9c52d4; expires=Sun, 07-Jan-2074 21:10:42 GMT; path=/; secure; HttpOnly
x-ms-service-request-id: 1ee13aa7-47f3-4a75-95fa-2916775a1f79
Strict-Transport-Security: max-age=31536000; includeSubDomains
REQ_ID: 1ee13aa7-47f3-4a75-95fa-2916775a1f79
CRM.ServiceId: framework
AuthActivityId: 0b562cc3-56f6-44f0-a26e-4039cfc4be6a
x-ms-dop-hint: 48
x-ms-ratelimit-time-remaining-xrm-requests: 1,200.00
x-ms-ratelimit-burst-remaining-xrm-requests: 5999
OData-Version: 4.0
X-Source: 110212218438874147222728177124203420477168182861012399121919014511175711948418152
Public: OPTIONS,GET,HEAD,POST
Set-Cookie: ARRAffinity=f60cbee43b7af0a5f322e7ce57a018546ed978f67f0c11cbb5e15b02ddb091a915134d20c556b0b34b9b6ae43ec3f5dcdad61788de889ffc592af7aca85fc1c508DC0FC94CB062A12107345846; path=/; secure; HttpOnly
X-Source: 2302101791355821068628523819830862152291172232072372448021147103846182145238216119
Date: Sun, 07 Jan 2024 21:10:42 GMT
Content-Length: 277

{"@odata.context":"https://yourorg.api.crm.dynamics.com/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.WhoAmIResponse","BusinessUnitId":"1647bf36-e90a-4c4d-9b61-969d57ce7a66","UserId":"24e34f5e-7f1a-43fe-88da-7e4b862d51ad","OrganizationId":"648e8efd-db86-466e-a5bc-a4d5eb9c52d4"}

Wenn Fiddler nicht läuft, erhalten Sie eine Fehlermeldung:

Invoke-RestMethod: C:\scripts\test.ps1:8:1
Line |
   8 |  Invoke-RestMethod `
     |  ~~~~~~~~~~~~~~~~~~~
     | No connection could be made because the target machine actively refused it.

Wenn Sie alle Ihre Invoke-RestMethod Anrufe über eine einzige Funktion weiterleiten möchten, wie etwa die unter Invoke-ResilientRestMethod Dienstschutzgrenzen verwalten beschriebene Dataverse Funktion, können Sie in der Datei einige Variablen festlegen, um diese Option an einer einzigen Stelle zu konfigurieren. Core.ps1

# <a name="set-to-true-only-while-debugging-with-fiddler"></a>Set to true only while debugging with Fiddler
$debug = $true
# <a name="set-this-value-to-the-fiddler-proxy-url-configured-on-your-computer"></a>Set this value to the Fiddler proxy URL configured on your computer
$proxyUrl = 'http://127.0.0.1:8888'

Innerhalb Ihrer zentralisierten Funktion können Sie den -Proxy Parameter mit Splatting festlegen und $request Hashtabelle nur beim Debuggen mit Fiddler verwenden.

function Invoke-ResilientRestMethod {
   param (
      [Parameter(Mandatory)] 
      $request,
      [bool]
      $returnHeader
   )

   if ($debug) {
      $request.Add('Proxy', $proxyUrl)
   }

   ...

Erfahren Sie mehr über die Erfassung von Webdatenverkehr mit Fiddler

Das CSDL-$metadata-Dokument für die Dataverse-Web-API herunterladen

Die $metadata der Common Schema Definition Language (CSDL) sind die wahre Quelle der Dataverse Web-API-Funktionen. Sie können sie in einem Browser anzeigen. Eventuell ist es jedoch einfacher, die Datei herunterzuladen und innerhalb von Visual Studio Code anzuzeigen. Das folgende Skript ist eine modifizierte Version des Skripts, das in Schnellstart-Web-API mit PowerShell eingeführt wurde. Der Unterschied besteht darin, dass das Invoke-WebRequest-Cmdlet verwendet wird, das besser zum Herunterladen eines XML-Dokuments geeignet ist.

$environmentUrl = 'https://yourorg.crm.dynamics.com/' # change to your organization
$writeFileTo =  'C:\temp\yourorg.xml' # change to your organization

## <a name="login-if-not-already-logged-in"></a>Login if not already logged in
if ($null -eq (Get-AzTenant -ErrorAction SilentlyContinue)) {
   Connect-AzAccount | Out-Null
}
# <a name="get-an-access-token"></a>Get an access token
$token = (Get-AzAccessToken -ResourceUrl $environmentUrl).Token
# <a name="common-headers"></a>Common headers
$xmlHeaders = @{
   'Authorization'    = 'Bearer ' + $token
   'Accept'           = 'application/xml'
   'OData-MaxVersion' = '4.0'
   'OData-Version'    = '4.0'
}

$doc = [xml](Invoke-WebRequest `
      -Uri ($environmentUrl + 'api/data/v9.2/$metadata?annotations=true') `
      -Method 'Get' `
      -Headers $xmlHeaders ).Content

$StringWriter = New-Object System.IO.StringWriter
$XmlWriter = New-Object System.XMl.XmlTextWriter $StringWriter
$xmlWriter.Formatting = 'indented'
$xmlWriter.Indentation = 2
$doc.WriteContentTo($XmlWriter)
$XmlWriter.Flush()
$StringWriter.Flush()
Set-Content -Path $writeFileTo -Value $StringWriter.ToString()
code $writeFileTo
  1. Kopieren Sie das Skript.
  2. Bearbeiten Sie die $environmentUrl- und $writeFileTo-Variablen je nach Ihren Anforderungen.
  3. Führen Sie das Skript im Visual Studio Code aus.

Das Dataverse Web-API-CSDL-$metadata-Dokument wird im Visual Studio Code geöffnet.

Möglicherweise erhalten Sie eine Benachrichtigung mit dem Inhalt: Aus Leistungsgründen sind Dokumentsymbole auf 5.000 Elemente begrenzt. Wenn ein neues Limit festgelegt wird, schließen Sie diese Datei und öffnen Sie sie erneut, um die Dokumentsymbole neu zu berechnen.

Die Benachrichtigung bietet die Möglichkeit, das Limit für die Visual Studio Code-XML-Erweiterung xml.symbols.maxItemsComputed zu ändern. Für die meisten Dataverse Web-API-CSDL-$metadata-Dokumente sollte es ausreichen, das Limit auf 500000 festzulegen.

Problembehandlung

Dieser Abschnitt enthält einige Hinweise zu Problemen, auf die Sie möglicherweise stoßen.

Fehlerdialog: Verbinden Sie ENOENT\\.\pipe\<RANDOM_text> mit der Schaltfläche „Launch.json öffnen“

Dieser Fehler kann beim Debuggen mit Visual Studio Code auftreten. So beheben Sie den Fehler:

  1. Wählen Sie Ansicht > Befehlspalette ... aus dem Visual Studio Code-Menü oder drücken Sie Strg+Umschalttaste+P.
  2. Geben Sie restart ein und wählen Sie Powershell: Restart session aus. Weitere Informationen finden Sie unter PowerShell/vscode-powershell-GitHub-Issue 4332.

Nächste Schritte,

Erfahren Sie mehr über Dataverse-Web-API-Funktionen durch Verständnis der Servicedokumente.

Überprüfen Sie den Beispielcode und führen Sie ihn aus.