为 DocumentDb 查询 post 创建授权负载的正确方法是什么?
What is the correct way to create the authorization payload for a DocumentDb query post?
我正在评估 DocumentDb 并尝试通过 DocumentDb Rest 提交查询 api。
当我尝试 post 查询时,我收到以下错误消息:
"The input content is invalid because the required properties - 'id; ' - are missing"
"The request payload is invalid. Ensure to provide a valid request payload."
这似乎表明 collection 中的文档没有 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)
);
}
此代码是 Access Control on DocumentDB Resources 文档中示例代码的变体。 页面上给出的示例代码存在相当大的缺陷,因为它使用了一个函数 GenerateMasterKeyAuthorizationSignature
,但该函数的名称与此示例类似 GenerateAuthToken
。它还仅显示针对 api 的 GET 请求,并不指示如何为 POST 请求创建授权令牌负载。这就是为什么我最关心的主要事情是授权 header。我不确定我是否正确设置了 resourceId 或 resourceType。如果我尝试一个空的 resourceId,这是我希望用于查询的,然后我会得到一个未经授权的响应,指示有效负载的预期 resourceId 是对 collection 的引用(collectionId如果我使用数据库的 _rid 和 collection,或者如果我使用名称,则为小写。
注意 引用的文档页面已更新,示例代码已删除。我还找到了其他参考来生成身份验证 header 并发现我正在正确创建它。
我在创建授权负载时是否使用了正确的 ResourceType 和 ResourceId 值?如果是,为什么我会收到有关所需 ID 属性 的错误消息?
如果我使用的值不正确,它们应该是什么?
决议更新
正如 Ryan 所指出的,问题出在 ContentType header 的 CharSet 属性 上。他的链接代码可能是更好的方法,但我还发现我可以创建 StringContent
的变量并在 posting 之前修改它以获得预期的结果。
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;
查询资源请参考 REST API 文档 - https://msdn.microsoft.com/en-us/library/azure/dn783363.aspx
您似乎缺少 ContentType header。
这必须设置为 "application/query+json" 用于查询。
即使您添加了它,我怀疑您接下来会遇到的问题是 .NET HttpClient 在执行 POST 时总是在此 header 的末尾添加一个字符集。从您使用 Encoding.UTF8 的代码中可以看出。
不幸的是,在 .NET 中(据我所知)没有简单的方法可以在不指定字符集的情况下执行 POST。
我创建了一个示例,其中包含执行此操作的 .NET HttpClient 的自定义扩展。
查看 "REST from .NET" 上发布的示例 github - https://github.com/Azure/azure-documentdb-dotnet/tree/master/samples/rest-from-.net
此示例将准确地向您展示应该使用什么值以及何时使用。
它对所有主要资源执行 GET 操作,并对执行查询执行 POST 操作。
我正在评估 DocumentDb 并尝试通过 DocumentDb Rest 提交查询 api。
当我尝试 post 查询时,我收到以下错误消息:
"The input content is invalid because the required properties - 'id; ' - are missing"
"The request payload is invalid. Ensure to provide a valid request payload."
这似乎表明 collection 中的文档没有 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)
);
}
此代码是 Access Control on DocumentDB Resources 文档中示例代码的变体。 页面上给出的示例代码存在相当大的缺陷,因为它使用了一个函数 GenerateMasterKeyAuthorizationSignature
,但该函数的名称与此示例类似 GenerateAuthToken
。它还仅显示针对 api 的 GET 请求,并不指示如何为 POST 请求创建授权令牌负载。这就是为什么我最关心的主要事情是授权 header。我不确定我是否正确设置了 resourceId 或 resourceType。如果我尝试一个空的 resourceId,这是我希望用于查询的,然后我会得到一个未经授权的响应,指示有效负载的预期 resourceId 是对 collection 的引用(collectionId如果我使用数据库的 _rid 和 collection,或者如果我使用名称,则为小写。
注意 引用的文档页面已更新,示例代码已删除。我还找到了其他参考来生成身份验证 header 并发现我正在正确创建它。
我在创建授权负载时是否使用了正确的 ResourceType 和 ResourceId 值?如果是,为什么我会收到有关所需 ID 属性 的错误消息?
如果我使用的值不正确,它们应该是什么?
决议更新
正如 Ryan 所指出的,问题出在 ContentType header 的 CharSet 属性 上。他的链接代码可能是更好的方法,但我还发现我可以创建 StringContent
的变量并在 posting 之前修改它以获得预期的结果。
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;
查询资源请参考 REST API 文档 - https://msdn.microsoft.com/en-us/library/azure/dn783363.aspx
您似乎缺少 ContentType header。 这必须设置为 "application/query+json" 用于查询。
即使您添加了它,我怀疑您接下来会遇到的问题是 .NET HttpClient 在执行 POST 时总是在此 header 的末尾添加一个字符集。从您使用 Encoding.UTF8 的代码中可以看出。
不幸的是,在 .NET 中(据我所知)没有简单的方法可以在不指定字符集的情况下执行 POST。
我创建了一个示例,其中包含执行此操作的 .NET HttpClient 的自定义扩展。
查看 "REST from .NET" 上发布的示例 github - https://github.com/Azure/azure-documentdb-dotnet/tree/master/samples/rest-from-.net
此示例将准确地向您展示应该使用什么值以及何时使用。 它对所有主要资源执行 GET 操作,并对执行查询执行 POST 操作。