Partager via


Utiliser PowerShell et Visual Studio Code avec l’API web Dataverse

Cet article développe l’article Démarrage rapide : API web avec PowerShell pour décrire les fonctionnalités avancées utilisant PowerShell et Visual Studio Code avec l’API web Dataverse pour :

Notes

Les instructions de cet article devraient fonctionner pour Windows, Linux et macOS, mais ces étapes n’ont été testées que sous Windows. Si des modifications sont nécessaires, faites-le nous savoir en utilisant la section Commentaires au bas de cet article.

Conditions préalables

Le contenu de cet article présente les mêmes conditions préalables que l’article Démarrage rapide : API web avec PowerShell .

Installer ou vérifier que les éléments suivants sont installés

Vérifier l’installation

  1. Ouvrez Visual Studio Code.

  2. Dans le menu Terminal, sélectionnez Nouveau terminal.

  3. Dans le volet de navigation Visual Studio Code, sélectionnez l’icône pour l’extension PowerShell.

  4. Copiez et collez le script suivant dans la fenêtre du terminal Visual Studio Code :

    Write-Host 'PowerShell Version:'$PSVersionTable.PSVersion.ToString()
    Write-Host 'PowerShell Az version:'(Get-InstalledModule Az).Version
    
  5. Appuyez sur Entrée. La sortie ressemblerait à l’exemple suivant :

    PowerShell Version: 7.4.0
    PowerShell Az version: 11.1.0
    

Si vous ne voyez pas de résultats comme celui-ci, installez ou mettez à jour les prérequis.

Ce dont vous avez besoin

  • Compte d’utilisateur valide pour un environnement Dataverse
  • URL vers l’environnement Dataverse auquel vous souhaitez vous connecter. Voir Afficher les ressources des développeurs pour savoir comment les trouver. Cela ressemble à ceci : https://yourorg.crm.dynamics.com/, où yourorg.crm est différent.
  • Présentation de base du langage de script PowerShell

Créer des fonctions réutilisables

Démarrage rapide : API web avec PowerShell a présenté comment authentifier et appeler la fonction WhoAmI avec Visual Studio Code. Cette approche peut suffire à tester ponctuellement une ou plusieurs opérations. Cependant, à mesure que vos scripts deviennent plus complexes, vous pourriez vous retrouver à taper le même code encore et encore.

Dans cette section, nous commençons à créer un ensemble de fonctions réutilisables dans des fichiers séparés auxquels nous pouvons accéder à l’aide du dot sourcing. Utilisez le dot sourcing pour charger un fichier contenant des scripts PowerShell pouvant contenir des fonctions et des variables qui font partie de la portée du script local.

Conseil

Vous pouvez trouver des définitions entièrement documentées de ces fonctions et bien plus encore dans notre référentiel d’exemples GitHub PowerApps sur PowerApps-Samples/dataverse/webapi/PS/

Créer une fonction Connect

Mettons le code auquel s’authentifier à Dataverse dans une fonction appelée Connect à l’intérieur d’un fichier nommé Core.ps1 afin de pouvoir le réutiliser dans une seule ligne de code.

  1. Créez un dossier. Dans cet exemple, nous créons un dossier dans C:\scripts.

  2. Ouvrez le dossier scripts dans Visual Studio Code.

  3. Créez un fichier texte dans le dossier des scripts nommé Core.ps1.

  4. Copiez et collez la fonction Connect suivante dans le fichier Core.ps1.

    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/'
    }
    

    Notes

    Le script ajoute les variables baseURI et baseHeaders au contexte global à l’aide du $global modificateur de portée afin qu’elles soient disponibles pour d’autres scripts dans la même session.

  5. Créez un autre fichier texte dans Visual Studio Code nommé test.ps1 dans votre scripts dossier.

  6. Copiez et collez le script suivant dans le fichier test.ps1 :

    . $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 en haut du fichier, dot sourcing utilise pour diriger le script afin de charger le contenu de ce fichier.

    N’oubliez pas de modifier https://yourorg.crm.dynamics.com/ pour qu’il corresponde à l’URL de votre environnement.

  7. Pour exécuter le script, appuyez sur F5.

    Le résultat pourrait ressembler à ce résultat :

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

Créer une fonction WhoAmI

Mettons le code pour appeler la fonction WhoAmI dans une fonction appelée Get-WhoAmI à l’intérieur d’un fichier nommé CommonFunctions.ps1 afin que nous puissions taper simplement 11 caractères au lieu de 100 à chaque fois que vous souhaitez utiliser la fonction WhoAmI

  1. Créez un fichier texte nommé CommonFunctions.ps1 dans votre dossier scripts.

  2. Copiez et collez la définition de fonction suivante dans CommonFunctions.ps1.

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

    Notes

    Cette définition de fonction utilise une technique appelée Splatting. Le Splatting rend vos commandes plus courtes et plus faciles à lire, car il transmet une collection de valeurs de paramètres à une commande en tant qu’unité.

  3. Enregistrez le fichier CommonFunctions.ps1.

  4. Modifiez le fichier pour qu’il ressemble au script suivant : test.ps1

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

    N’oubliez pas de modifier la valeur https://yourorg.crm.dynamics.com/ pour qu’elle corresponde à l’URL de votre environnement.

  5. Pour exécuter le script, appuyez sur F5.

    La sortie devrait ressembler exactement à ce qu’elle était auparavant.

Créer des fonctions d’opérations de table

Mettons les fonctions pour effectuer des opérations de table courantes dans un fichier nommé TableOperations.ps1 afin que nous puissions les réutiliser.

  1. Créez un fichier texte nommé TableOperations.ps1 dans votre dossier scripts.

  2. Copiez et collez les définitions de fonction suivantes dans 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
    }
    
    

    Pour obtenir des informations sur la façon de composer ces requêtes, consultez les articles suivants :

  3. Enregistrez le fichier TableOperations.ps1.

  4. Copiez et collez le code suivant dans le fichier test.ps1.

    . $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"
    

    N’oubliez pas de modifier la valeur https://yourorg.crm.dynamics.com/ pour qu’elle corresponde à l’URL de votre environnement.

  5. Pour exécuter le script, appuyez sur F5.

    Le résultat pourrait ressembler à ce résultat :

    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
    

Gérer les exceptions

Jusqu’à présent, dans cet article, vous avez copié et collé le code qui vous est fourni. Mais lorsque vous commencez à écrire et à utiliser vos propres fonctions, vous pouvez rencontrer des erreurs. Lorsque ces erreurs se produisent, elles peuvent provenir de Dataverse ou de votre script.

Ajoutez une fonction d’assistance qui peut aider à détecter la source des erreurs et à extraire les détails pertinents des erreurs renvoyées par Dataverse.

  1. Ajoutez la fonction suivante au fichier : Invoke-DataverseCommands Core.ps1

    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
          $_
       }
    }
    

    La fonction Invoke-DataverseCommands utilise l’applet de commande Invoke-Command pour traiter un ensemble de commandes dans un bloc try/catch. Toutes les erreurs renvoyées par Dataverse sont des erreurs HttpResponseException, donc le premier bloc catch écrit un message An error occurred calling Dataverse: au terminal avec les données d’erreur JSON.

    Les données JSON dans $_.ErrorDetails.Message contiennent des caractères Unicode d’échappement. Par exemple : \u0026 au lieu de & et \u0027 au lieu de '. Cette fonction inclut du code qui remplace ces caractères par les caractères sans échappement afin qu’ils correspondent exactement aux erreurs que vous voyez ailleurs.

    Sinon, les erreurs sont réécrites dans la fenêtre du terminal avec un message : An error occurred in the script:

  2. Enregistrez le fichier Core.ps1.

  3. Modifiez le fichier pour ajouter le script suivant qui utilise une valeur de paramètre non valide. test.ps1 setName Le paramètre doit être account . accounts Cette erreur est courante.

    . $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
    
    }
    

    N’oubliez pas de modifier la valeur https://yourorg.crm.dynamics.com/ pour qu’elle corresponde à l’URL de votre environnement.

  4. Pour exécuter le script, appuyez sur F5.

    Le résultat pourrait ressembler à ce résultat :

    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. Modifiez le fichier test.ps1 pour générer une erreur de script dans le bloc Invoke-DataverseCommands :

    Invoke-DataverseCommands {
    
       throw 'A script error'
    
    }
    
  6. Pour exécuter le script, appuyez sur F5.

    Le résultat devrait être presque le même que s’il n’était pas inclus dans le bloc Invoke-DataverseCommands :

    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
    

Gérer les limites de protection de service Dataverse

Les limites de l’API de protection des services Dataverse permettent de garantir que Dataverse propose une disponibilité et des performances constantes. Lorsque les applications clientes font des demandes extraordinaires sur les ressources du serveur à l’aide de l’API Web, Dataverse renvoie 429 Too Many Requests des erreurs et les applications clientes doivent suspendre leurs opérations pendant la durée spécifiée dans l’en-tête Retry-After.

L’applet de commande Invoke-RestMethod PowerShell Paramètre MaximumRetryCount spécifie combien de fois PowerShell réessaye une demande lorsqu’un code d’échec est compris entre 400 et 599 inclus ou que le code 304 est reçu. Cela signifie que PowerShell réessaye en cas d’erreur de type 429 de protection de services Dataverse lorsque vous incluez une valeur pour ce paramètre. Le paramètre MaximumRetryCount peut être utilisé avec RetryIntervalSec pour spécifier le nombre de secondes à attendre. La valeur par défaut est 5 secondes. Si la réponse à l’erreur inclut un en-tête Retry-After pour une erreur 429, comme les erreurs de protection de services Dataverse se produisent, cette valeur est utilisée à la place.

Vous ne rencontrerez peut-être jamais d’erreur de limite de protection de service pendant que vous apprenez à utiliser l’API web Dataverse avec PowerShell. Cependant, les scripts que vous écrivez peuvent envoyer un grand nombre de requêtes qui génèrent des erreurs. Découvrez donc comment les gérer au mieux à l’aide de PowerShell.

Si vous ajoutez le paramètre MaximumRetryCount à chaque appel Dataverse en utilisant Invoke-RestMethod, PowerShell réessaye un large éventail d’erreurs. Réessayer chaque erreur ralentit vos scripts, en particulier lors du développement et des tests. Vous devrez attendre 10 à 15 secondes à chaque fois qu’une erreur se produit, selon le nombre de tentatives que vous spécifiez. Une approche alternative consiste à encapsuler la Invoke-RestMethod dans votre propre méthode qui gère les tentatives pour des erreurs spécifiques.

La fonction suivante prend un objet de table de hachage comme paramètre obligatoire et un indicateur booléen Invoke-ResilientRestMethod pour indiquer s’il faut ou non renvoyer l’en-tête réponse. request returnHeader Lorsque $returnHeader est vrai, il envoie la requête à l’aide de la Invoke-RestMethod commande avec le paramètre ResponseHeadersVariable pour capturer les en-têtes renvoyés. La fonction utilise Out-Null donc la sortie qui représente le corps réponse vide n’est pas renvoyée avec la fonction. Sinon, la fonction envoie la requête en utilisant Invoke-RestMethod l’objet request et renvoie le corps réponse.

Si Invoke-RestMethod échoue avec une erreur 429, elle vérifie si l’objet request a une propriété MaximumRetryCount. Si la fonction réussit, elle crée une MaximumRetryCount propriété définie sur 3. La Invoke-RestMethod relance est ensuite effectuée à l’aide de l’objet de requête et de la valeur d’en-tête Retry-After réponse. Si l’indicateur returnHeader est défini sur True, il renvoie l’en-tête de réponse. Si Invoke-RestMethod échoue avec une autre erreur, elle renvoie l’exception.

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 $_
   }
}

Vous pouvez utiliser une fonction similaire dans vos fonctions réutilisables. Lorsque les fonctions doivent renvoyer des valeurs de l’en-tête de la réponse, elles doivent définir la valeur returnHeader sur $true. Par exemple, la fonction suivante New-Record modifie l’exemple de fonction dans Fonctions d’opérations de création de table pour l’utiliser Invoke-ResilientRestMethod au lieu de Invoke-RestMethod directement.

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())
}

Sinon, Invoke-ResilientRestMethod peut remplacer le Invoke-RestMethod comme indiqué dans cet exemple Get-Record :

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
}

La seule différence est que vous transmettez la table de hachage ($RetrieveRequest) à la méthode au lieu d’utiliser le splatting (@RetrieveRequest). Sinon, vous obtenez une erreur de script : A parameter cannot be found that matches parameter name 'Headers'.

Déboguer avec Fiddler

Fiddler est un proxy de débogage Web utilisé pour afficher le trafic HTTP sur votre ordinateur. L’affichage de ces données est utile lors du débogage des scripts. Par défaut, les requêtes et réponses HTTP envoyées à l’aide de l’applet de commande Invoke-RestMethod ne sont pas visibles lorsque vous utilisez Fiddler.

Pour afficher le trafic HTTP dans Fiddler, définissez le Invoke-RestMethod paramètre Proxy sur l’URL configurée comme proxy Fiddler sur votre ordinateur local. Par défaut, l’URL est http://127.0.0.1:8888. Votre URL peut être différente.

Par exemple, si vous appelez la fonction WhoAmI avec le paramètre -Proxy défini pendant que Fiddler capture le trafic :

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

Dans Fiddler, vous pouvez voir tous les détails :

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

Si Fiddler n’est pas en cours d’exécution, vous obtenez une erreur :

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

Si vous choisissez d’acheminer tous vos appels via une seule fonction, telle que celle décrite dans la section Invoke-RestMethod Gérer les limites de protection des services Invoke-ResilientRestMethod , vous pouvez définir certaines variables dans le fichier pour configurer cette option à un seul emplacement. Dataverse 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'

Dans votre fonction centralisée, vous pouvez définir le paramètre avec splatting et utiliser le table de hachage uniquement lors du débogage avec Fiddler. -Proxy $request

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

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

   ...

Découvrir comment capturer le trafic Web avec Fiddler

Télécharger le document $metadata CSDL de l’API web Dataverse

Les métadonnées du langage CSDL (Common Schema Definition Language) sont la source de vérité sur les fonctionnalités de l’API Web. Dataverse Vous pouvez l’afficher dans un navigateur, mais il vous sera peut-être plus facile de télécharger le fichier et de l’afficher dans Visual Studio Code. Le script suivant est une version modifiée du script introduit dans Démarrage rapide : API web avec PowerShell. La différence est qu’il utilise l’applet de commande Invoke-WebRequest, qui est plus appropriée pour télécharger un document XML.

$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. Copiez le script.
  2. Modifiez les variables $environmentUrl et $writeFileTo en fonction de vos besoins.
  3. Exécutez le script dans Visual Studio Code.

Le document $metadata CSDL de l’API Web s’ouvre dans Dataverse Code. Visual Studio

Vous pourriez recevoir une notification indiquant : Pour des raisons de performances, les symboles de document sont limités à 5 000 éléments. Si une nouvelle limite est définie, Fermer et rouvrez ce fichier pour recalculer les symboles de document.

La notification donne la possibilité de modifier la Visual Studio limite d’extension du code XML xml.symbols.maxItemsComputed . Pour la plupart des documents de métadonnées CSDL de l’API Web, la définition de la limite à devrait être suffisante. Dataverse 500000

Résolution des problèmes

Cette section contient des conseils sur les problèmes que vous pourriez rencontrer.

Boîte de dialogue d’erreur : connectez ENOENT\\.\pipe\<RANDOM_text> avec le bouton Ouvrir ’launch.json’

Cette erreur peut se produire lors du débogage avec Visual Studio Code. Pour résoudre l’erreur :

  1. Sélectionnez Afficher > Palette de commandes... dans le menu Visual Studio Code ou appuyez sur Ctrl+Maj+P.
  2. Saisissez restart et sélectionnez Powershell: Restart session. Consultez PowerShell/vscode-powershell Problème GitHub 4332 pour plus d’informations.

Étapes suivantes

En savoir plus sur les fonctionnalités de l’API web Dataverse en comprenant les documents de service.

Examinez et exécutez un exemple de code.