You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PowerShell REST API查询Azure表存储未获取续传令牌怎么办?

解决Azure表存储REST API查询无法获取续传令牌的问题

你的代码未捕获响应头或正确读取响应体中的续传信息,导致无法获取下一批条目的令牌。Azure表存储返回超过1000条实体时,会通过两种方式提供续传数据:

  • 响应头包含x-ms-continuation-NextPartitionKey和x-ms-continuation-NextRowKey字段
  • JSON响应体的@odata.nextLink字段提供下一页完整请求URL

而当前使用Invoke-RestMethod默认仅返回响应体,不会保留响应头,也未检查响应体中的续传链接。


修正方案1:捕获响应头提取续传令牌

添加-ResponseHeadersVariable参数捕获响应头,从中提取续传键或直接读取响应体的@odata.nextLink:

$RGName = "MyResourceGroup"
$StorageAccount = "MyStorageAccount"
$TableName = "MyTable"
$SA = Get-AzStorageAccount -Name $StorageAccount -ResourceGroupName $RGName -verbose
$Accesskey = ($SA | Get-AzStorageAccountKey).value[0]

$Version = "2021-08-06"
$Table_url = "https://$StorageAccount.table.core.windows.net/$TableName"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$stringToSign = "$GMTTime`n/$StorageAccount/$TableName"
$HMACSHA = New-Object System.Security.Cryptography.HMACSHA256
$HMACSHA.key = [Convert]::FromBase64String($Accesskey)
$Signature = $HMACSHA.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
$Signature = [Convert]::ToBase64String($Signature)
$Headers = @{
    'x-ms-date'    = $GMTTime
    Authorization  = "SharedKeyLite " + $StorageAccount + ":" + $Signature
    "x-ms-version" = $Version
    Accept         = "application/json;odata=fullmetadata"
}

# 调用API时捕获响应头到$respHeaders变量
$Item = Invoke-RestMethod -Method GET -Uri $Table_url -Headers $Headers -ContentType application/json -ResponseHeadersVariable respHeaders

# 从响应头提取续传令牌并构建下一页URL
if ($respHeaders['x-ms-continuation-NextPartitionKey']) {
    $nextPartitionKey = $respHeaders['x-ms-continuation-NextPartitionKey']
    $nextRowKey = $respHeaders['x-ms-continuation-NextRowKey']
    $nextUrl = "$Table_url?NextPartitionKey=$nextPartitionKey&NextRowKey=$nextRowKey"
    Write-Host "下一页请求URL:$nextUrl"
}

# 或直接读取响应体中的@odata.nextLink
if ($Item.'@odata.nextLink') {
    Write-Host "下一页请求URL:$($Item.'@odata.nextLink')"
}

修正方案2:自动循环获取所有条目

通过循环迭代,自动使用续传链接获取所有条目:

$RGName = "MyResourceGroup"
$StorageAccount = "MyStorageAccount"
$TableName = "MyTable"
$SA = Get-AzStorageAccount -Name $StorageAccount -ResourceGroupName $RGName -verbose
$Accesskey = ($SA | Get-AzStorageAccountKey).value[0]

$Version = "2021-08-06"
$currentUrl = "https://$StorageAccount.table.core.windows.net/$TableName"
$allItems = @()

while ($currentUrl) {
    $GMTTime = (Get-Date).ToUniversalTime().toString('R')
    # 签名使用固定路径/$StorageAccount/$TableName,不受URL查询参数影响
    $stringToSign = "$GMTTime`n/$StorageAccount/$TableName"
    $HMACSHA = New-Object System.Security.Cryptography.HMACSHA256
    $HMACSHA.key = [Convert]::FromBase64String($Accesskey)
    $Signature = $HMACSHA.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
    $Signature = [Convert]::ToBase64String($Signature)
    $Headers = @{
        'x-ms-date'    = $GMTTime
        Authorization  = "SharedKeyLite " + $StorageAccount + ":" + $Signature
        "x-ms-version" = $Version
        Accept         = "application/json;odata=fullmetadata"
    }

    $response = Invoke-RestMethod -Method GET -Uri $currentUrl -Headers $Headers -ContentType application/json -ResponseHeadersVariable respHeaders
    $allItems += $response.value

    # 优先使用响应体中的@odata.nextLink作为下一页URL
    $currentUrl = $response.'@odata.nextLink'
    # 若不存在则从响应头构建下一页URL
    if (-not $currentUrl -and $respHeaders['x-ms-continuation-NextPartitionKey']) {
        $nextPartitionKey = $respHeaders['x-ms-continuation-NextPartitionKey']
        $nextRowKey = $respHeaders['x-ms-continuation-NextRowKey']
        $currentUrl = "$Table_url?NextPartitionKey=$nextPartitionKey&NextRowKey=$nextRowKey"
    }
}

Write-Host "总共获取到$($allItems.Count)条条目"

关键注意点

  • 每次发起下一页请求时,必须重新生成签名(x-ms-date为当前UTC时间,签名需随时间更新)
  • 续传信息的存在形式取决于Accept头设置,application/json;odata=fullmetadata会返回@odata.nextLink,同时响应头也会包含续传键

内容的提问来源于stack exchange,提问作者Lars Panzerbjrn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 04:01:39