Freigeben über


Verwenden von PowerShell und Visual Studio Code mit der Dataverse-Web-API

In diesem Artikel erfahren Sie, wie Sie PowerShell und Visual Studio Code mit der Dataverse-Web-API für erweiterte Funktionen verwenden. Sie lernen, wiederverwendbare Funktionen zu erstellen, Ausnahmen zu behandeln und Dienstschutzgrenzwerte zu verwalten.

Hinweis

Die Anweisungen in diesem Artikel sollten für Windows, Linux und macOS funktionieren, diese Schritte werden jedoch nur unter Windows getestet. Wenn Änderungen erforderlich sind, verwenden Sie den Abschnitt "Feedback " am Ende dieses Artikels.

Voraussetzungen

Dieser Artikel enthält die gleichen Voraussetzungen wie die Schnellstart-Web-API mit PowerShell-Artikel .

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 Menü "Terminal " die Option "Neues Terminal" aus.

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

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

    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

Erstellen wiederverwendbarer Funktionen

Die Schnellstart-Web-API mit PowerShell hat eingeführt, wie die WhoAmI-Funktion mit Visual Studio Code authentifiziert und aufgerufen wird. Dieser Ansatz kann alles sein, was Sie für einen Ad-hoc-Test eines oder mehrerer Vorgänge benötigen. Da Ihre Skripts jedoch komplexer werden, können Sie denselben Code immer wieder eingeben.

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

Tipp

Sie finden vollständig dokumentierte Definitionen dieser Funktionen und vieles mehr im GitHub-PowerApps-Samples-Repository unter PowerApps-Samples/dataverse/webapi/PS/

Erstellen einer Connect-Funktion

Platzieren Sie den Code in einer Funktion namens Connect innerhalb einer Datei Core.ps1, um sich bei Dataverse zu authentifizieren, damit Sie ihn in einer einzigen Codezeile wiederverwenden können.

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

  2. Öffnen Sie den Skriptordner in Visual Studio Code.

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

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

    function Connect {
       param (
          [Parameter(Mandatory)] 
          [String] 
          $environmentUrl
       )
    
       ## Login interactively if not already logged in
       if ($null -eq (Get-AzTenant -ErrorAction SilentlyContinue)) {
          Connect-AzAccount | Out-Null
       }
    
       # Get an access token
       $secureToken = (Get-AzAccessToken `
          -ResourceUrl $environmentUrl `
          -AsSecureString).Token
    
       # Convert the secure token to a string
       $token = ConvertFrom-SecureString `
          -SecureString $secureToken `
          -AsPlainText
    
       # 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 = $environmentUrl + 'api/data/v9.2/'
    }
    

    Hinweis

    Das Skript fügt die baseURI Variablen und baseHeaders Variablen mithilfe des $globalBereichsmodifizierers zum globalen Kontext hinzu, sodass andere Skripts in derselben Sitzung sie verwenden können.

  5. Erstellen Sie in Visual Studio Code eine weitere Textdatei mit dem Namen test.ps1, die sich im Ordner scripts befindet.

  6. Kopieren Sie das folgende Skript, und fügen Sie es in die test.ps1 Datei ein:

    . $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 wird dot sourcing verwendet, um das Skript zum Laden des Inhalts dieser Datei zu leiten.

    Vergessen Sie nicht, https://yourorg.crm.dynamics.com/ zu ändern, damit es mit der URL Ihrer Umgebung übereinstimmt.

  7. Drücken Sie F5, um das Skript auszuführen.

    Die Ausgabe sieht möglicherweise ähnlich wie diese aus:

    PS C:\scripts> . 'C:\scripts\test.ps1'
    {
    "@odata.context": "https://yourorg.crm.dynamics.com/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.WhoAmIResponse",
    "BusinessUnitId": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
    "UserId": "22cc22cc-dd33-ee44-ff55-66aa66aa66aa",
    "OrganizationId": "00aa00aa-bb11-cc22-dd33-44ee44ee44ee"
    }
    

Erstellen einer WhoAmI-Funktion

Platzieren Sie den Code zum Aufruf der Funktion WhoAmI in einer Funktion namens Get-WhoAmI, innerhalb einer Datei namens CommonFunctions.ps1. Auf diese Weise müssen Sie nur 11 Zeichen anstelle von 100 Zeichen eingeben, wenn Sie die WhoAmI-Funktion verwenden möchten.

  1. Erstellen Sie eine neue Textdatei mit dem Namen CommonFunctions.ps1 in Ihrem scripts-Ordner.

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

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

    Hinweis

    Diese Funktionsdefinition verwendet eine Technik namens Splatting. Splatting macht Ihre Befehle kürzer und leichter lesbar, da es eine Sammlung von Parameterwerten als Einheit an einen Befehl übergibt.

  3. Speichern Sie die Datei CommonFunctions.ps1.

  4. Ändern Sie die test.ps1 Datei 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 der URL für Ihre Umgebung entspricht.

  5. Drücken Sie F5, um das Skript auszuführen.

    Die Ausgabe sollte genau wie zuvor aussehen.

Tabellenoperationen erstellen

Platzieren Sie Funktionen, die allgemeine Tabellenvorgänge in einer Datei TableOperations.ps1 ausführen, sodass Sie sie wiederverwenden können.

  1. Erstellen Sie eine neue Textdatei mit dem Namen TableOperations.ps1 in Ihrem scripts-Ordner.

  2. Kopieren Sie die folgenden Funktionsdefinitionen, und fügen Sie sie in TableOperations.ps1.

    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 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 der URL für Ihre Umgebung entspricht.

  5. Drücken Sie F5, um das Skript auszuführen.

    Die Ausgabe sieht möglicherweise ähnlich wie diese aus:

    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 in diesem Artikel haben Sie Code kopiert und eingefügt, der für Sie bereitgestellt wurde. Wenn Sie jedoch mit dem Schreiben und Verwenden eigener Funktionen beginnen, können Fehler auftreten. Wenn diese Fehler auftreten, können sie von Dataverse oder Ihrem Skript stammen.

Fügen Sie eine Hilfsfunktion hinzu, mit der sie die Quelle der Fehler erkennen und relevante Details aus Fehlern extrahieren können, die von Dataverse zurückgegeben werden.

  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 fehler, die von Dataverse zurückgegeben werden, sind HttpResponseException Fehler, sodass der erste catch Block eine An error occurred calling Dataverse: Nachricht mit den JSON-Fehlerdaten in das Terminal schreibt.

    Die JSON-Daten in $_.ErrorDetails.Message enthalten einige maskierte Unicode-Zeichen. Beispiel: \u0026 anstelle von & und \u0027 anstelle von '. 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 mit einer Meldung in das Terminalfenster zurückgeschrieben: 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 sein accounts. Dieser Fehler ist häufig.

    . $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 der URL für Ihre Umgebung entspricht.

  4. Drücken Sie F5, um das Skript auszuführen.

    Die Ausgabe sieht möglicherweise ähnlich wie diese aus:

    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 auszuwerfen:

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

    Die Ausgabe sollte fast identisch sein, als wenn sie nicht im Invoke-DataverseCommands Block enthalten war:

    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
    

Verwalten von Datenschutzgrenzwerten für Dataverse-Dienste

Dataverse Service Protection API-Grenzwerte tragen dazu bei, sicherzustellen, dass Dataverse eine konsistente Verfügbarkeit und Leistung bietet. Wenn Clientanwendungen mithilfe der Web-API außergewöhnliche Anforderungen an Serverressourcen stellen, gibt Dataverse 429 Zu viele Anfragen zurück. Clientanwendungen müssen Vorgänge für die dauer anhalten, die im Retry-After-Header angegeben ist.

Der Parameter "MaximumRetryCount" des PowerShell -Invoke-RestMethod-Cmdlets gibt an, wie oft powerShell eine Anforderung wiederholt, wenn sie einen Fehlercode zwischen 400 und 599 (einschließlich) oder 304 empfängt. Dieser Parameter bedeutet, dass PowerShell Dataverse-Dienstschutz 429-Fehler erneut anzeigt, wenn Sie einen Wert dafür angeben. Verwenden Sie den MaximumRetryCount Parameter mit " RetryIntervalSec ", 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, wie Dataverse-Dienstschutzfehler tun, wird dieser Wert stattdessen verwendet.

Möglicherweise tritt niemals ein Dienstschutzgrenzwertfehler auf, während Sie erfahren, wie Sie die Dataverse-Web-API mit PowerShell verwenden. Skripts, die Sie schreiben, senden jedoch möglicherweise eine große Anzahl von Anforderungen, die Fehler erzeugen, und erfahren Sie, wie Sie diese mithilfe von PowerShell am besten verwalten können.

Wenn Sie den MaximumRetryCount-Parameter zu jedem Dataverse-Aufruf mit Invoke-RestMethod hinzufügen, probiert PowerShell eine Vielzahl von Fehlern erneut. Beim Wiederholen jedes Fehlers werden Ihre Skripts langsam, insbesondere beim Entwickeln und Testen. Sie müssen bei jedem Auftreten eines Fehlers 10 bis 15 Sekunden warten, je nachdem, wie viele Wiederholungen Sie angeben. Ein alternativer Ansatz besteht darin, die Invoke-RestMethod innerhalb Ihrer eigenen Methode einzuschließen, die Neuversuche bei bestimmten Fehlern verwaltet.

Die folgende Invoke-ResilientRestMethod Funktion verwendet ein request Hashtable-Objekt als obligatorischen Parameter und ein boolesches returnHeader Flag, um anzugeben, ob der Antwortheader zurückgegeben werden soll. Wenn $returnHeader wahr ist, sendet es die Anforderung mithilfe des Invoke-RestMethod-Befehls mit dem Parameter ResponseHeadersVariable, um die zurückgegebenen Header zu erfassen. Die Funktion verwendet Out-Null , sodass die Ausgabe, die den leeren Antworttext darstellt, nicht von der Funktion zurückgegeben wird. Andernfalls sendet die Funktion die Anforderung mithilfe von Invoke-RestMethod und dem request-Objekt und gibt den Antwortkörper zurück.

Wenn der Invoke-RestMethod mit einem 429-Fehler fehlschlägt, wird überprüft, ob das request-Objekt eine MaximumRetryCount-Eigenschaft hat. Wenn die Funktion erfolgreich ist, wird eine Eigenschaft MaximumRetryCount erstellt und auf 3 gesetzt. Ein Wiederholungsversuch von Invoke-RestMethod wird anschließend unter Verwendung des Anforderungsobjekts und des Retry-After-Antwortheaderwerts ausgeführt. Wenn das returnHeader Flag "true" 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 der Kopfzeile einer Antwort zurückgeben müssen, sollten sie den returnHeader-Wert auf $true setzen. Die folgende New-Record Funktion ändert z. B. die Beispielfunktion in "Funktionen zur Erstellung von Tabellenoperationen", sodass sie Invoke-ResilientRestMethod anstelle von Invoke-RestMethod direkt verwendet.

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

   }
   # 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 Invoke-ResilientRestMethod das Invoke-RestMethod wie in diesem Beispiel Get-Record gezeigt ersetzen.

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
   }
   # 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 wird ein Skriptfehler angezeigt: A parameter cannot be found that matches parameter name 'Headers'.

Debuggen mithilfe von Fiddler

Fiddler ist ein Webdebuggingproxy, mit dem Sie HTTP-Datenverkehr auf Ihrem Computer anzeigen können. Das Anzeigen dieser Daten ist beim Debuggen von Skripts hilfreich. Standardmäßig zeigt Fiddler keine HTTP-Anforderungen und -Antworten an, die Sie mithilfe des CmdletsInvoke-RestMethod senden.

Um DEN HTTP-Datenverkehr in Fiddler anzuzeigen, legen Sie den Invoke-RestMethodProxyparameter auf die URL fest, die als Fiddler-Proxy auf Ihrem lokalen Computer konfiguriert ist. Standardmäßig ist die URL http://127.0.0.1:8888. Ihre URL kann unterschiedlich sein.

Wenn Sie beispielsweise die WhoAmI-Funktion mit dem -Proxy Parametersatz 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 werden alle Details angezeigt:

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=00aa00aa-bb11-cc22-dd33-44ee44ee44ee; 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":"11bb11bb-cc22-dd33-ee44-55ff55ff55ff","UserId":"22cc22cc-dd33-ee44-ff55-66aa66aa66aa","OrganizationId":"00aa00aa-bb11-cc22-dd33-44ee44ee44ee"}

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 sich dafür entscheiden, alle Aufrufe Invoke-RestMethod über eine einzelne Funktion weiterzuleiten, z. B. die Invoke-ResilientRestMethod unter "Verwalten von Datenverse-Dienstschutzbeschränkungen" beschriebenen, können Sie einige Variablen in der Core.ps1 Datei festlegen, um diese Option an einem einzigen Speicherort zu konfigurieren.

# Set to true only while debugging with Fiddler
$debug = $true
# 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 die $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 das Erfassen von Webdatenverkehr mit Fiddler

Herunterladen des CsDL-$metadata-Dokuments der Dataverse-Web-API

Die Common Schema Definition Language (CSDL) $metadata ist die verlässliche Quelle für die Dataverse-Web-API-Funktionen. Sie können sie in einem Browser anzeigen, aber möglicherweise ist es einfacher, die Datei herunterzuladen und in Visual Studio Code anzuzeigen. Das folgende Skript ist eine geänderte Version des Skripts, das in der Schnellstart-Web-API mit PowerShell eingeführt wurde. Der Unterschied besteht darin, dass das CmdletInvoke-WebRequest verwendet wird, das für das Herunterladen eines XML-Dokuments besser geeignet ist.

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

## Login if not already logged in
if ($null -eq (Get-AzTenant -ErrorAction SilentlyContinue)) {
   Connect-AzAccount | Out-Null
}
# Get an access token
$secureToken = (Get-AzAccessToken `
   -ResourceUrl $environmentUrl `
   -AsSecureString).Token

# Convert the secure token to a string
$token = ConvertFrom-SecureString `
   -SecureString $secureToken `
   -AsPlainText


# 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 variablen, $writeFileTo um Ihren Anforderungen zu entsprechen.
  3. Führen Sie das Skript in Visual Studio Code aus.

Das CsDL-$metadata-Dokument der Dataverse-Web-API wird in Visual Studio Code geöffnet.

Möglicherweise erhalten Sie eine Benachrichtigung, die besagt: Aus Leistungsgründen sind Dokumentsymbole auf 5.000 Elemente beschränkt. Wenn ein neuer Grenzwert festgelegt ist, schließen Sie diese Datei, und öffnen Sie sie erneut, um Dokumentsymbole neu zu komppilieren.

Die Benachrichtigung bietet die Möglichkeit, den XML-Erweiterungsgrenzwert xml.symbols.maxItemsComputed von Visual Studio Code zu ändern. Für die meisten Dataverse-Web-API-CSDL-$metadata Dokumente sollte das Festlegen des Grenzwerts 500000 ausreichen.

Problembehandlung

Dieser Abschnitt enthält einige Anleitungen für Probleme, die auftreten können.

Fehlerdialog: ENOENT\\.\pipe\<RANDOM_text> mit der Schaltfläche zum Öffnen von „Launch.json“ verbinden

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

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

Nächste Schritte

Erfahren Sie mehr über die Funktionen der Dataverse-Web-API durch Lesen der Servicedokumente.

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