通过


将 PowerShell 和 Visual Studio Code 与 Dataverse Web API 配合使用

本文介绍如何将 PowerShell 和 Visual Studio Code 与 Dataverse Web API 配合使用,实现高级功能。 你将了解如何创建可重用函数、处理异常以及管理服务保护限制。

注释

本文中的说明适用于 Windows、Linux 和 macOS,但这些步骤仅在 Windows 上进行测试。 如果需要更改,请使用本文底部的 “反馈 ”部分。

先决条件

本文的先决条件与 PowerShell 文章中的快速入门 Web API 相同。

安装或验证是否已安装以下内容

验证安装

  1. 打开Visual Studio代码。

  2. 在“终端”菜单中,选择“新终端”。

  3. 在 Visual Studio Code 导航窗格中,选择 PowerShell 扩展的图标。

  4. 在Visual Studio Code终端窗口中复制并粘贴以下脚本:

    Write-Host 'PowerShell Version:'$PSVersionTable.PSVersion.ToString()
    Write-Host 'PowerShell Az version:'(Get-InstalledModule Az).Version
    
  5. Enter。 输出应如下所示:

    PowerShell Version: 7.4.0
    PowerShell Az version: 11.1.0
    

如果未看到如下所示的结果,请安装或更新必备组件。

还需要

  • Dataverse 环境的有效用户帐户
  • 要连接到的 Dataverse 环境的 URL。 请参阅 “查看开发人员资源 ”,了解如何查找它。 它看起来如下所示:https://yourorg.crm.dynamics.com/,其中 yourorg.crm 不同。
  • 基本了解 PowerShell 脚本语言

创建可重用函数

使用 PowerShell 快速入门 Web API 介绍了如何使用 Visual Studio Code 对 WhoAmI 函数 进行身份验证和调用。 对于一个或多个操作的临时测试,这种方法可能就足够了。 但是,随着脚本变得更加复杂,你可能会发现自己再次键入相同的代码。

在本节中,您将开始在单独的文件中创建一组可重用的函数,您可以通过点引用访问这些函数。 使用点引用加载包含 PowerShell 脚本的文件,这些脚本中的函数和变量将作为本地脚本作用域的一部分。

小窍门

可以在 PowerApps-Samples/dataverse/webapi/PS/GitHub PowerApps-Samples 存储库中找到这些函数的完整文档定义和其他内容

创建 Connect 函数

将认证到 Dataverse 的代码放入一个名为Connect的函数中,并存放在名为Core.ps1的文件,以便可以在一行代码中重复使用它。

  1. 创建文件夹。 在此示例中,在 . 中创建 C:\scripts一个文件夹。

  2. 在 Visual Studio Code 中打开脚本文件夹

  3. 在脚本文件夹中创建一个名为 Core.ps1 的文本文件。

  4. 将以下 Connect 函数复制并粘贴到 Core.ps1 文件中。

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

    注释

    该脚本使用baseURIbaseHeaders将变量$global添加到全局上下文,以便同一会话中的其他脚本可以使用它们。

  5. 在 Visual Studio Code 的 scripts 文件夹中创建一个名为 test.ps1 的文本文件。

  6. 将以下脚本复制并粘贴到 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 使用点引用指示脚本加载该文件的内容。

    请记得更改 https://yourorg.crm.dynamics.com/ 以匹配环境的 URL。

  7. 若要运行脚本,请按 F5

    输出可能类似于以下输出:

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

创建 WhoAmI 函数

将调用CommonFunctions.ps1的代码放在名为CommonFunctions.ps1的文件中的函数内。 这样,每次想要使用 WhoAmI 函数时,只需键入 11 个字符而不是 100 个字符。

  1. 在您的scripts文件夹中创建一个名为CommonFunctions.ps1的新文本文件。

  2. CommonFunctions.ps1中复制并粘贴以下函数定义。

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

    注释

    此函数定义使用了一种称为展开的技术。 拆分能让命令更简洁、更易读,因为它将一组参数值作为一个整体传递给命令。

  3. 保存 CommonFunctions.ps1 文件。

  4. 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
    

    请记得将 https://yourorg.crm.dynamics.com/ 值更改为与您环境对应的 URL。

  5. 若要运行脚本,请按 F5

    输出应与以前完全相同。

创建表操作函数

将执行常见表操作的函数放入名为 TableOperations.ps1 的文件中,以便重复使用它们。

  1. scripts文件夹中创建一个名为TableOperations.ps1的新文本文件。

  2. 将以下函数定义复制并粘贴到 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
    }
    
    

    有关如何撰写这些请求的信息,请参阅以下文章:

  3. 保存 TableOperations.ps1 文件。

  4. 复制以下代码并将其粘贴到 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"
    

    请记住更改https://yourorg.crm.dynamics.com/的值,以匹配您环境的 URL。

  5. 若要运行脚本,请按 F5

    输出可能类似于以下输出:

    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
    

处理异常

本文到目前为止,你复制并粘贴了为你提供的代码。 但是,当你开始编写和使用自己的函数时,可能会遇到错误。 发生这些错误时,它们可能来自 Dataverse 或脚本。

添加帮助程序函数,帮助检测错误的来源,并从 Dataverse 返回的错误中提取相关详细信息。

  1. 将以下 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
          $_
       }
    }
    

    Invoke-DataverseCommands 函数使用 Invoke-Command cmdlettry/catch 块中处理一组命令。 Dataverse 返回的任何错误都是 HttpResponseException 错误,因此第一个 catch 代码块会将包含 JSON 错误数据的 An error occurred calling Dataverse: 消息写入终端。

    $_.ErrorDetails.Message 中的 JSON 数据包含一些转义的 Unicode 字符。 例如: \u0026 而不是 & 而不是 \u0027'。 此函数包含一些代码,会将这些字符替换为未转义的字符,以便它们与您在其他地方看到的错误完全一致。

    否则,错误信息将被写回到终端窗口,并显示如下消息:An error occurred in the script:

  2. 保存 Core.ps1 文件。

  3. 编辑 test.ps1 文件,添加以下使用无效 setName 参数值的脚本。 参数 account 应为 accounts. 此错误很常见

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

    请记住更改https://yourorg.crm.dynamics.com/的值,以匹配您环境的 URL。

  4. 若要运行脚本,请按 F5

    输出可能类似于以下输出:

    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. 编辑test.ps1文件,在Invoke-DataverseCommands块中抛出脚本错误:

    Invoke-DataverseCommands {
    
       throw 'A script error'
    
    }
    
  6. 若要运行脚本,请按 F5

    输出结果应与未包含在 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
    

管理 Dataverse 服务保护限制

Dataverse 服务保护 API 限制有助于确保 Dataverse 提供一致的可用性和性能。 当客户端应用程序使用 Web API 对服务器资源提出异常要求时,Dataverse 将返回 429 个请求错误 。 客户端应用程序必须在 Retry-After 标头中指定的持续时间内暂停操作。

PowerShell Invoke-RestMethod cmdletMaximumRetryCount 参数 指定在收到 400 到 599 之间的故障代码(含)或 304 之间的失败代码时,PowerShell 重试请求的次数。 此参数表示,当您为其设置值时,PowerShell 将重试 Dataverse 服务保护 429 错误。 将 MaximumRetryCount 参数与 RetryIntervalSec 一起使用,以指定要等待的秒数。 默认值为 5 秒。 如果错误响应包含 429 错误的 Retry-After 标头(如 Dataverse 服务保护错误那样),则使用该值代替。

在了解如何将 Dataverse Web API 与 PowerShell 配合使用时,可能永远不会遇到服务保护限制错误。 但是,你编写的脚本可能会发送大量生成错误的请求,因此了解如何使用 PowerShell 最好地管理这些请求。

如果您通过 Invoke-RestMethodMaximumRetryCount 参数添加到每个 Dataverse 调用中,PowerShell 会重试各种类型的错误。 重试每个错误会使脚本变慢,尤其是在开发和测试时。 每次发生错误时,都需要等待 10 到 15 秒,具体取决于指定的重试次数。 另一种方法是将 Invoke-RestMethod 封装在您自己的方法中,用于管理特定错误的重试。

以下 Invoke-ResilientRestMethod 函数采用 request 哈希表对象作为必需参数和布尔 returnHeader 标志,以指示是否返回响应标头。 如果 $returnHeader 为 true,它将使用 Invoke-RestMethod 带有 ResponseHeadersVariable 参数的命令发送请求,以捕获返回的标头。 该函数使用 Out-Null ,因此该函数不会返回表示空响应正文的输出。 否则,该函数使用Invoke-RestMethodrequest对象发送请求并返回响应正文。

Invoke-RestMethod如果失败并出现 429 错误,它将检查对象是否request具有MaximumRetryCount属性。 如果函数成功,它将创建一个 MaximumRetryCount 属性并将其设置为 3。 随后,系统将使用请求对象和 Retry-After 响应标头值重试 Invoke-RestMethod 操作。 returnHeader如果标志为 true,则返回响应标头。 如果 Invoke-RestMethod 因其他任何错误失败,则会重新抛出异常。

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

可以在可重用函数中使用类似的函数。 当函数需要从响应的标头返回值时,它们需要将 returnHeader$true设置为 。 例如,以下New-Record函数对创建表操作函数中的示例函数进行修改,以使用Invoke-ResilientRestMethod而不是直接使用Invoke-RestMethod

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

否则,Invoke-ResilientRestMethod 可以替换 Invoke-RestMethod,如在以下示例 Get-Record 中所示:

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
}

唯一的区别在于,您将哈希表 ($RetrieveRequest) 传递给方法,而不是使用展开 (@RetrieveRequest)。 否则,会收到脚本错误: A parameter cannot be found that matches parameter name 'Headers'.

使用 Fiddler 进行调试

Fiddler 是一个 Web 调试代理,可用于查看计算机上的 HTTP 流量。 调试脚本时,查看此数据非常有用。 默认情况下,Fiddler 不会显示使用 Invoke-RestMethod cmdlet 发送的 HTTP 请求和响应。

若要查看 Fiddler 中的 HTTP 流量,请将 Invoke-RestMethodProxy 参数 设置为在本地计算机上配置为 Fiddler 代理的 URL。 默认情况下,该 URL 为 http://127.0.0.1:8888。 URL 可能有所不同。

例如,当 Fiddler 正在捕获流量时,如果您调用 WhoAmI 函数并设置了 -Proxy 参数:

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

在 Fiddler 中,可以看到所有详细信息:

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

如果 Fiddler 未运行,则会出现错误:

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

如果选择通过单个函数(如 Invoke-RestMethodInvoke-ResilientRestMethod中所述)路由所有调用,则可以在Core.ps1文件中设置一些变量,以在单个位置配置此选项。

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

在集中式函数中,您可以通过展开参数设置 -Proxy 参数,仅在使用 Fiddler 调试时才使用 $request 哈希表。

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

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

   ...

了解如何使用 Fiddler 捕获 Web 流量

下载 Dataverse Web API CSDL $metadata 文档

公共架构定义语言(CSDL)$metadata 是关于 Dataverse Web API 功能的可信来源。 可以在浏览器中查看它,但你可能会发现下载文件并在 Visual Studio Code 中查看该文件更容易。 以下脚本是 PowerShell 快速入门 Web API 中引入的脚本的修改版本。 区别在于它使用 Invoke-WebRequest cmdlet,该 cmdlet 更适合下载 XML 文档。

$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. 复制脚本。
  2. 请编辑$environmentUrl$writeFileTo变量以匹配您的需求。
  3. 在 Visual Studio Code 中运行脚本。

Dataverse Web API CSDL $metadata 文档将在 Visual Studio Code 中打开。

你可能会收到一条通知,指出: 出于性能原因,文档符号限制为 5,000 个项目。如果设置了新限制,请关闭并重新打开此文件以重新计算文档符号

通知提供更改 Visual Studio Code XML 扩展 xml.symbols.maxItemsComputed 限制的选项。 对于大多数 Dataverse Web API CSDL $metadata 文档,将限制设置为 500000 就足够了。

故障排除

本部分包含你可能遇到的问题的一些指南。

错误对话:连接 ENOENT\\.\pipe\<RANDOM_text> 和 Open 'launch.json' 按钮

使用 Visual Studio Code 进行调试时,可能会出现此错误。 若要解决该错误:

  1. 从 Visual Studio Code 菜单中选择 “查看>命令面板...” ,或按 Ctrl+Shift+P
  2. 键入 restart 并选择 Powershell: Restart session。 有关详细信息,请参阅 PowerShell/vscode-powershell GitHub 问题 4332

后续步骤

通过了解服务文档详细了解 Dataverse Web API 功能。

查看并运行示例代码。