I am evaluating DocumentDb and attempting to submit a query via the DocumentDb Rest api.
When I attempt to post the query, I'm receiving the following error messages:
"The input content is invalid because the required properties - 'id; ' - are missing"
"The request payload is invalid. Ensure to provide a valid request payload."
this seems to indicate that the documents in the collection do not have an id property, but here's my current test data that I retrieved from the azure portal which you can see have an id property:
[
{
"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"
}
]
Here is the code I'm currently attempting to use (note that the actual values have been replaced with dummy data):
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)
);
}
This code is a variation of the example code from the documentation of Access Control on DocumentDB Resources. The example code given on the page is rather flawed in that it uses a function GenerateMasterKeyAuthorizationSignature
but the function is named GenerateAuthToken
like this example. It also only displays GET requests against the api, and doesn't indicate how the authorization token payload is created for a POST request. Which is why the main thing I'm most concerned with is the authorization header. I'm not convinced I'm setting the resourceId or resourceType correctly. If I try an empty resourceId which is what I would expect to use for a query, I then get an unauthorized response that indicates the expected resourceId of the payload is a reference to the collection (the collectionId in lower case if I'm using _rid's of the database and collection, or the named path if I use names).
note the referenced documentation page has since been updated and the sample code has been removed. I also found other references to generate the auth header and found I am creating it correctly.
Am I using the correct ResourceType and ResourceId values when creating the authorization payload? If I am, why am I getting the error about the required id property?
If I’m not using the correct values, what should they be?
Resolution Update
As Ryan has indicated, the issue is with the CharSet property of the ContentType header. His linked code is probably a better way to do it, but I also found that I could create a variable of the StringContent
and modify it before posting to get the expected results.
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;