为 DocumentDb 查询帖子创建授权有效负载的正确方法是什么

本文关键字:负载 是什么 方法 有效 授权 查询 DocumentDb 创建 | 更新日期: 2023-09-27 18:37:02

我正在评估DocumentDb并尝试通过DocumentDb Rest api提交查询。

当我尝试发布查询时,我收到以下错误消息:

"The input content is invalid because the required properties - 'id; ' - are missing"
"The request payload is invalid. Ensure to provide a valid request payload."

这似乎表明集合中的文档没有 id 属性,但这是我从 Azure 门户检索的当前测试数据,可以看到它有一个 id 属性:

[
  {
    "id": "c1058415-8e03-49e2-8c41-97bc902ebfb0",
    "name": "Add a thing",
    "description": "Ad a thing to the list"
  },
  {
    "id": "0f88f4af-7afc-4928-a60f-c3546b28e243",
    "name": "find another thing",
    "description": "find another thing"
  },
  {
    "id": "b669dbc6-6056-4392-a898-4d846e6c0126",
    "name": "stuff goes there",
    "description": "stuff goes there"
  }
]

这是我目前正在尝试使用的代码(请注意,实际值已替换为虚拟数据):

private static readonly string MasterKey = "my master key";
private static readonly Uri BaseUri = new Uri("https://mydocdb.documents.azure.com");
private static bool _useNames = true;
private static readonly string DatabaseId = _useNames ? "MyDatabase" : "prdnAA==";
private static readonly string CollectionId = _useNames ? "MyCollection" : "prdnAKFrZAA=";
private static readonly string UtcDate = DateTime.UtcNow.ToString("r");
private static void Main(string[] args)
{
    var client = new HttpClient();
    client.DefaultRequestHeaders.Add("x-ms-date", UtcDate);
    client.DefaultRequestHeaders.Add("x-ms-documentdb-isquery", "True");
    client.DefaultRequestHeaders.Add("x-ms-version", "2015-08-06");
    string verb = "POST";
    string resourceType = "docs";
    string resourceLink = $"dbs/{DatabaseId}/colls/{CollectionId}/docs";
    string resourceId = _useNames ? $"dbs/{DatabaseId}/colls/{CollectionId}" : $"{CollectionId}";
    string authHeader = GenerateAuthToken(verb, resourceId, resourceType, MasterKey, "master", "1.0");
    client.DefaultRequestHeaders.Remove("authorization");
    client.DefaultRequestHeaders.Add("authorization", authHeader);
    var response = client.PostAsync(
        new Uri(BaseUri, resourceLink),
        new StringContent("{'"query'":'"SELECT * FROM root '"}",
            Encoding.UTF8,
            "application/query+json")
        ).Result;
    string result = response.Content.ReadAsStringAsync().Result;
    Console.WriteLine(result);
}
private static string GenerateAuthToken(string verb, string resourceId, string resourceType, string key,
    string keyType, string tokenVersion)
{
    var verbInput = verb ?? "";
    var resourceIdInput = resourceId ?? "";
    var resourceTypeInput = resourceType ?? "";
    var payLoad = string.Format(CultureInfo.InvariantCulture, 
        "{0}'n{1}'n{2}'n{3}'n{4}'n",
        verbInput.ToLowerInvariant(),
        resourceTypeInput.ToLowerInvariant(),
        resourceIdInput,
        UtcDate.ToLowerInvariant(),
        ""
        );
    var hmacSha256 = new HMACSHA256 { Key = Convert.FromBase64String(key) };
    var hashPayLoad = hmacSha256.ComputeHash(Encoding.UTF8.GetBytes(payLoad));
    var signature = Convert.ToBase64String(hashPayLoad);
    return HttpUtility.UrlEncode(
        string.Format(
            CultureInfo.InvariantCulture,
            "type={0}&ver={1}&sig={2}",
            keyType,
            tokenVersion,
            signature)
        );
}

此代码是 DocumentDB 资源上的访问控制文档中示例代码的变体。页面上给出的示例代码存在相当大的缺陷,因为它使用了一个函数GenerateMasterKeyAuthorizationSignature但该函数的名称GenerateAuthToken,就像这个例子一样。它还仅显示针对 API 的 GET 请求,并且不指示如何为 POST 请求创建授权令牌有效负载。这就是为什么我最关心的主要事情是授权标头。我不相信我正确设置了资源 ID 或资源类型。如果我尝试一个空的 resourceId,这是我期望用于查询的,然后我会收到一个未经授权的响应,指示有效负载的预期 resourceId 是对集合的引用(如果我使用数据库和集合的_rid则为小写的 collectionId,如果我使用 names,则为命名路径)。

注意 引用的文档页面已更新,示例代码已删除。我还找到了其他引用来生成 auth 标头,并发现我正在正确创建它。

创建授权有效负载时,我是否使用了正确的资源类型和资源 ID 值?如果是,为什么会收到有关所需 id 属性的错误?

如果我没有使用正确的值,它们应该是什么?

分辨率更新

正如 Ryan 所指出的,问题出在 ContentType 标头的 CharSet 属性上。他的链接代码可能是一种更好的方法,但我也发现我可以创建一个StringContent变量并在发布之前对其进行修改以获得预期的结果。

var stringQuery = new StringContent("{'"query'":'"SELECT * FROM root '"}",
    Encoding.UTF8,
    "application/query+json");
stringQuery.Headers.ContentType.CharSet = null;
HttpResponseMessage response = client.PostAsync(new Uri(BaseUri, resourceLink)
    ,stringQuery).Result;

为 DocumentDb 查询帖子创建授权有效负载的正确方法是什么

请参考 REST API 文档查询资源 - https://msdn.microsoft.com/en-us/library/azure/dn783363.aspx

看起来您缺少内容类型标头。对于查询,必须将其设置为"application/query+json"。

即使您添加了它,我怀疑您接下来会遇到的问题是 .NET HttpClient 在执行 POST 时总是在此标头的末尾添加一个字符集。从使用 Encoding.UTF8 的代码中可以看出。

不幸的是,.NET(我知道)中没有简单的方法可以在不指定字符集的情况下进行 POST。

我创建了一个示例,其中包含执行此操作的 .NET HttpClient 的自定义扩展。

看看发布在github上的"REST来自.NET"示例 - https://github.com/Azure/azure-documentdb-dotnet/tree/master/samples/rest-from-.net

此示例将准确显示应使用哪些值以及何时使用。它对所有主要资源执行 GET 以及用于执行查询的 POST。