Use PowerShell y Visual Studio Code con la API web de Dataverse

Este artículo amplía el artículo Inicio rápido de API web con PowerShell para describir capacidades avanzadas usando PowerShell y Visual Studio Code con la API web de Dataverse para:

Nota

Las instrucciones de este artículo deberían funcionar para Windows, Linux y macOS, pero estos pasos solo se han probado en Windows. Si se necesitan cambios, háganoslo saber utilizando la sección Comentario al final de este artículo.

Requisitos previos

El contenido de este artículo tiene los mismos requisitos previos que el artículo Inicio rápido de API web con Powershell.

Instale o verifique que lo siguiente esté instalado

Verificar instalación

  1. Abra Visual Studio Code.

  2. En el menú Terminal, seleccione Nueva terminal.

  3. En el panel de navegación de Visual Studio Code, seleccione el icono para la extensión de PowerShell.

  4. Copie y pegue el script siguiente en la ventana de la terminal de Visual Studio Code:

    Write-Host 'PowerShell Version:'$PSVersionTable.PSVersion.ToString()
    Write-Host 'PowerShell Az version:'(Get-InstalledModule Az).Version
    
  5. Presione Entrar. El resultado debería similar al siguiente:

    PowerShell Version: 7.4.0
    PowerShell Az version: 11.1.0
    

Si no ve resultados como este, instale o actualice los requisitos previos.

También necesitará

  • Una cuenta de usuario válida para un entorno de Dataverse
  • La dirección URL al entorno de Dataverse al que quiere conectarse. Consulte Ver recursos para desarrolladores para saber cómo encontrarlo. Se parece a esto: https://yourorg.crm.dynamics.com/, donde yourorg.crm es diferente.
  • Comprensión básica de lenguaje de scripting PowerShell

Crear funciones reutilizables

Inicio rápido de API web con PowerShell presentó cómo autenticar y llamar a la función WhoAmI con Visual Studio Code. Este enfoque podría ser todo lo que necesita para una prueba ad hoc de una o más operaciones. Sin embargo, a medida que sus scripts se vuelven más complejos, es posible que se encuentre escribiendo el mismo código una y otra vez.

En esta sección, comenzamos a crear un conjunto de funciones reutilizables en archivos separados a los que podemos acceder usando dot sourcing. Utilice el origen de puntos para cargar un archivo que contenga scripts de PowerShell que puedan contener funciones y variables que pasen a formar parte del alcance del script local.

Sugerencia

Puede encontrar definiciones completamente documentadas de estas funciones y más en nuestro GitHub PowerApps- Repositorio de muestras en PowerApps-Samples/dataverse/webapi/PS/

Crear una función Connect

Pongamos el código para autenticarnos en Dataverse en una función llamada Connect dentro de un archivo llamado Core.ps1 para que podamos reutilizarlo en una sola línea de código.

  1. Cree una carpeta. En este ejemplo, creamos una carpeta en C:\scripts.

  2. Abra la carpeta de scripts usando Visual Studio Code.

  3. Cree un archivo de texto en la carpeta de scripts denominada Core.ps1.

  4. Copie y pegue la siguiente función Connect en el archivo 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/'
    }
    

    Nota

    El script agrega las variables baseURI y baseHeaders al contexto global usando el modificador de ámbito $global para que estén disponibles para otros scripts. en la misma sesión.

  5. Cree otro archivo de texto usando Visual Studio Code llamado test.ps1 en su carpeta scripts.

  6. Copie y pegue el siguiente script en el archivo test.ps1:

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

    . $PSScriptRoot\Core.ps1 en la parte superior del archivo se muestra el uso de fuentes de puntos para indicar al script que cargue el contenido de ese archivo.

    Recuerde cambiar el valor https://yourorg.crm.dynamics.com/ para que coincida con la URL de su entorno.

  7. Pulse F5 para ejecutar el script.

    El resultado deberá ser ahora similar a esto:

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

Crear una función WhoAmI

Pongamos el código para invocar la función WhoAmI en una función llamada Get-WhoAmI dentro de un archivo llamado CommonFunctions.ps1 para que podamos escribir solo 11 caracteres en lugar de 100 cada vez que desee utilizar la función WhoAmI

  1. Cree un archivo de texto nuevo llamado CommonFunctions.ps1 en su carpeta scripts.

  2. Copie y pegue la siguiente definición de función en CommonFunctions.ps1.

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

    Nota

    Esta definición de función utiliza una técnica llamada splatting. Splatting hace que sus comandos sean más cortos y más fáciles de leer porque pasa una colección de valores de parámetros a un comando como una unidad.

  3. Guarde el archivo CommonFunctions.ps1.

  4. Edite el archivo test.ps1, cambie el contenido para que se parezca al siguiente script:

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

    Recuerde cambiar el valor https://yourorg.crm.dynamics.com/ para que coincida con la URL de su entorno.

  5. Pulse F5 para ejecutar el script.

    La salida debería verse exactamente como antes.

Crear funciones de operaciones de tabla

Coloquemos funciones para realizar operaciones de tabla comunes en un archivo llamado TableOperations.ps1 para que podamos reutilizarlas.

  1. Cree un archivo de texto nuevo llamado TableOperations.ps1 en su carpeta scripts.

  2. Copie y pegue las siguientes definiciones de función en 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
    }
    
    

    Para obtener información acerca de cómo crear estas solicitudes, vea los artículos siguientes:

  3. Guarde el archivo TableOperations.ps1.

  4. Copie el siguiente código y péguelo en el cuadro archivo test.ps1.

    . $PSScriptRoot\Core.ps1
    . $PSScriptRoot\CommonFunctions.ps1
    . $PSScriptRoot\TableOperations.ps1
    
    Connect 'https://yourorg.crm.dynamics.com/' # change this
    
    # 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"
    

    Recuerde cambiar el valor https://yourorg.crm.dynamics.com/ para que coincida con la URL de su entorno.

  5. Pulse F5 para ejecutar el script.

    El resultado deberá ser ahora similar a esto:

    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
    

Administrar excepciones

Hasta ahora en este artículo ha estado copiando y pegando el código que se le proporcionó. Pero cuando empiece a escribir sus propias funciones y a usarlas, encontrará errores. Cuando ocurren estos errores, pueden ser de Dataverse o de su script.

Agreguemos una función auxiliar que pueda ayudar a detectar el origen de los errores y extraer detalles relevantes de los errores devueltos por Dataverse.

  1. Edite el archivo Core.ps1 para agregar la siguiente función Invoke-DataverseCommands:

    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 función Invoke-DataverseCommands utiliza el cmdlet Invoke-Command para procesar un conjunto de comandos dentro de un bloque try/catch. Cualquier error devuelto por Dataverse son errores HttpResponseException, por lo que el primer bloque catch escribe un mensaje An error occurred calling Dataverse: en la terminal con los datos de error JSON.

    Los datos JSON en $_.ErrorDetails.Message contienen algunos caracteres Unicode escapados. Por ejemplo: \u0026 en lugar de & y \u0027 en lugar de '. Esta función incluye código que reemplaza esos caracteres con caracteres sin escape para que coincidan exactamente con los errores que ve en otros lugares.

    De lo contrario, los errores se escriben en la ventana del terminal con un mensaje: An error occurred in the script:

  2. Guarde el archivo Core.ps1.

  3. Edite el archivo test.ps1 para utilizar el siguiente script que utiliza un valor de parámetro setName no válido. account debe ser accounts. Es un error común

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

    Recuerde cambiar el valor https://yourorg.crm.dynamics.com/ para que coincida con la URL de su entorno.

  4. Pulse F5 para ejecutar el script.

    El resultado deberá ser ahora similar a esto:

    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. Edite el archivo test.ps1 para generar un error de secuencia de comandos dentro del bloque Invoke-DataverseCommands:

    Invoke-DataverseCommands {
    
       throw 'A script error'
    
    }
    
  6. Pulse F5 para ejecutar el script.

    El resultado debería ser casi el mismo que si no estuviera incluido en el bloque 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
    

Administrar límites de protección de servicio de Dataverse

Límites de API de protección de servicios de Dataverse ayudar a garantizar que Dataverse proporcione disponibilidad y rendimiento constantes. Cuando las aplicaciones cliente exigen de forma extraordinaria los recursos del servidor utilizando la API web, Dataverse devuelve errores 429 Demasiadas solicitudes y la aplicación cliente debe pausar las operaciones durante el tiempo especificado en el Encabezado Reintentar después.

El PowerShell Cmdlet Invoke-RestMethod Parámetro MaximumRetryCount especifica cuántas veces PowerShell reintenta una solicitud cuando se recibe un código de error entre 400 y 599, inclusive o 304. Esto significa que PowerShell reintenta errores de protección de servicio 429 de Dataverse cuando se incluye un valor para este parámetro. El parámetro MaximumRetryCount se puede utilizar con RetryIntervalSec para especificar el número de segundos de espera. El valor predeterminado es de 5 segundos. Si la respuesta de error incluye un encabezado Retry-After para un error 429, como hacen los errores de protección del servicio de Dataverse, en su lugar se utiliza ese valor.

Es posible que nunca encuentre un error de límite de protección del servicio mientras aprende a utilizar la API web de Dataverse con PowerShell. Los scripts que escriba pueden usarse para enviar la gran cantidad de solicitudes necesarias para encontrar estos errores, por lo que debe saber que pueden ocurrir y cómo puede administrarlos usando PowerShell.

Si agrega el parámetro MaximumRetryCount a cada llamada a Dataverse usando Invoke-RestMethod, PowerShell reintenta una amplia gama de errores. Reintentar cada error hace que los scripts sean lentos, especialmente durante el desarrollo y las pruebas. Deberá esperar de 10 a 15 segundos cada vez que ocurra un error, dependiendo de cuántos reintentos especifique. Un enfoque alternativo es encapsular el Invoke-RestMethod en su propio método que gestiona los reintentos para errores específicos.

La siguiente función Invoke-ResilientRestMethod toma un objeto de tabla hash request como parámetro obligatorio y una bandera booleana returnHeader para indicar si se debe devolver el encabezado de respuesta o no. Cuando $returnHeader es verdadero, envía la solicitud usando el comando Invoke-RestMethod con el parámetro ResponseHeadersVariable para capturar los encabezados devueltos y usa Out-Null para que la salida que representa el cuerpo de respuesta vacío no se devuelva con la función. De lo contrario, la función envía la solicitud usando Invoke-RestMethod usando el objeto request y devuelve el cuerpo de la respuesta.

Si el Invoke-RestMethod falla con un error 429, verifica si el objeto request tiene una propiedad MaximumRetryCount. Si no, agrega uno con valor 3. Luego vuelve a intentar Invoke-RestMethod utilizando el objeto de solicitud y el valor del encabezado de respuesta Retry-After. Si el indicador returnHeader es verdadero, devuelve el encabezado de respuesta. Si Invoke-RestMethod falla con cualquier otro error, vuelve a lanzar la excepción.

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

Puede utilizar una función como esta en sus funciones reutilizables. Cuando las funciones necesitan devolver valores del encabezado de la respuesta, deben establecer el valor returnHeader en $true. Por ejemplo, la siguiente función New-Record modifica la función de ejemplo en Crear funciones de operaciones de tabla para usar Invoke-ResilientRestMethod en lugar de Invoke-RestMethod directamente.

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

De lo contrario, Invoke-ResilientRestMethod puede reemplazar el Invoke-RestMethod como se muestra en este ejemplo de 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 única diferencia es que pasa la tabla hash ($RetrieveRequest) al método en lugar de usar splatting (@RetrieveRequest). De lo contrario, obtendrá un error de secuencia de comandos: A parameter cannot be found that matches parameter name 'Headers'.

Corregir errores usando Fiddler

Fiddler es un proxy de depuración web que se utiliza para ver el tráfico HTTP en su ordenador. Ver estos datos es útil al depurar scripts. De forma predeterminada, las solicitudes y respuestas HTTP enviadas mediante el cmdlet Invoke-RestMethod no serán visibles con Fiddler.

Para ver el tráfico HTTP usando Fiddler, establezca el parámetro Proxy Invoke-RestMethod en la URL configurada como proxy de Fiddler en su computadora local. De forma predeterminada, la URL es http://127.0.0.1:8888. El suyo puede ser diferente.

Por ejemplo, si invoca la función WhoAmI con el parámetro -Proxy establecido mientras Fiddler está capturando tráfico:

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

En Fiddler podrá ver todos los detalles:

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 no se está ejecutando, recibirá un error como este:

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 elige enrutar todas sus llamadas Invoke-RestMethod a través de una única función, como la Invoke-ResilientRestMethod que se describe en Administrar límites de protección del servicio Dataverse, puede establecer algunas variables en el archivo Core.ps1 para configurar esta opción en una sola ubicación.

# <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'

Luego, dentro de su función centralizada puede configurar el parámetro -Proxy usando splatting con la tabla hash $request solo al depurar con Fiddler.

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

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

   ...

Obtenga más información sobre cómo capturar tráfico web con Fiddler

Descargar el documento $metadata CSDL de la API web de Dataverse

El $metadata CSDL es la fuente de verdad sobre las capacidades de la API web Dataverse. Puede verlo en un navegador, pero puede que le resulte más fácil descargar el archivo y verlo dentro de Visual Studio Code. El siguiente script es una versión modificada del script introducido en Inicio rápido de API web con PowerShell. La diferencia es que utiliza el cmdlet Invoke-WebRequest, que es más apropiado para descargar un documento XML.

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

## <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. Copie el script.
  2. Edite las variables $environmentUrl y $writeFileTo para satisfacer sus necesidades.
  3. Ejecute el script en Visual Studio Code.

El $metadata CSDL de la API web de Dataverse se abrirá en Visual Studio Code.

Probablemente recibirá una notificación que diga:

Por motivos de rendimiento, los símbolos de los documentos se han limitado a 5000 elementos. Si se establece un nuevo límite, cierre y vuelva a abrir este archivo para volver a calcular los símbolos del documento.

La notificación ofrece la opción de cambiar el límite de Visual Studio extensión XML xml.symbols.maxItemsComputed. Para la mayoría de los documentos Dataverse Web API CSDL $metadata, establecer el límite en 500 000 debería ser suficiente.

Solución de problemas

Esta sección contiene algunas pautas para los problemas que pueda encontrar.

Cuadro de diálogo de error: conecte ENOENT\\.\pipe\<RANDOM_text> con el botón Abrir 'launch.json'

Este error puede ocurrir en ocasiones al depurar usando Visual Studio Code. Para resolverlo:

  1. Seleccione Ver > Paleta de comandos... en el menú Visual Studio Code, o presione Ctrl+Mayús+P.
  2. Escriba restart y seleccione Powershell: Restart session. Consulte PowerShell/vscode-powershell GitHub Problema 4332 para obtener más información.

Pasos siguientes

Aprender más acerca de las Capacidades de API web de Dataverse mediante la comprensión de los documentos de servicio.

Revisar y ejecutar código de muestra.