1

I want to make an SQL Query to my Azure DocumentDb. I've got a quite messy code for now but that's how it looks

public string GetResources(string collection) {
    var client = new System.Net.Http.HttpClient();
    client.DefaultRequestHeaders.Add("x-ms-date", utc_date);
    client.DefaultRequestHeaders.Add("x-ms-version", "2015-08-06");
    client.DefaultRequestHeaders.Add("x-ms-documentdb-isquery", "True");
    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/query+json"));

    //GET a document
    var verb = "POST";
    var resourceType = "docs";
    var resourceLink = string.Format("dbs/{0}/colls/{1}/docs", databaseId, collection);
    var resourceId = (idBased) ? resourceLink : "";

    var authHeader = GenerateAuthToken(verb, resourceId, resourceType, masterKey, "master", "1.0");
    Console.WriteLine(authHeader);

    client.DefaultRequestHeaders.Remove("authorization");
    client.DefaultRequestHeaders.Add("authorization", authHeader);


    var q = new DbQuery {
        Query = "SELECT * FROM root"
    };
    var postData = new List<KeyValuePair<string, string>>();
    postData.Add(new KeyValuePair<string, string>("query", q.Query));
    return PostAsync(resourceLink, postData, client).Result;
}


public async Task<string> PostAsync(string uri, List<KeyValuePair<string, string>> data, HttpClient httpClient)
{
    var content = new FormUrlEncodedContent(data);
    Console.WriteLine(httpClient.DefaultRequestHeaders.Authorization);
    var response = await httpClient.PostAsync(new Uri(baseUri, uri), content);

    response.EnsureSuccessStatusCode();

    string postContent = await response.Content.ReadAsStringAsync();
    return await Task.Run(() => postContent);
}

string GenerateAuthToken(string verb, string resourceId, string resourceType, string key, string keyType, string tokenVersion)
{
    var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) };

    string verbInput = verb ?? "";
    string resourceIdInput = resourceId ?? "";
    string resourceTypeInput = resourceType ?? "";

    string payLoad = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}\n{1}\n{2}\n{3}\n{4}\n",
            verb.ToLowerInvariant(),
            resourceType.ToLowerInvariant(),
            resourceId,
            utc_date.ToLowerInvariant(),
            ""
    );

    byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad));
    string signature = Convert.ToBase64String(hashPayLoad);

    return System.Net.WebUtility.UrlEncode(String.Format(System.Globalization.CultureInfo.InvariantCulture, "type={0}&ver={1}&sig={2}",
        keyType,
        tokenVersion,
        signature));
}

I have a request to get a document by its Id and I use the same approach. It works fine. I believe the problem could be with my resourceLink but honestly I tried a lot of version and all with no result.. what am I missing here?

3
  • Have you tried formatting the content of the post as JSON rather than URL encoding? That would explain why your GET based upon an id works but this POST does not. On the other hand, the fact that you get a 401 indicates that the contents are not the problem, but the auth header is. Also, would you be willing to accept the answer on the other question where I suggested using the REST API? Commented Feb 15, 2016 at 20:01
  • thanks @LarryMaccherone! but am I right at least that "dbs/{0}/colls/{1}/docs" is a right path to make a query to get a list of documents? Commented Feb 16, 2016 at 9:01
  • 1
    i see you've used the sample code from our REST documentation. Maybe take a look at gist.github.com/ryancrawcour/cc5d898b924ebc9635d1 that will show you examples of doing various GET operations on various resources. Your select * from root, is the same as i am doing where i do a GET for all documents in a collection. I can extend this sample and add POST to it for doing actual queries if you like. Commented Feb 17, 2016 at 19:54

2 Answers 2

2

the value for resourceLink will depend on whether you are using id based routing, or rid based routing.

it looks like you are using id based routing so, for a query, the resourceLink should be

string.Format("dbs/{0}/colls/{1}/docs", databaseId, collectionId);

and resourceId should be the same,

string.Format("dbs/{0}/colls/{1}", databaseId, collectionId)

POST is a little different in that it requires specific headers set. The REST documentation for Querying DocumentDB resources using the REST API should document what is needed - https://msdn.microsoft.com/en-us/library/azure/dn783363.aspx

For further examples of what the resourceLink & resourceId and a working eample have a look at the REST from .NET samples we just published. https://github.com/Azure/azure-documentdb-dotnet/blob/d3f8e9c731bc92816d023719e7e780b7a9546ca2/samples/rest-from-.net/Program.cs#L151-L164

Sign up to request clarification or add additional context in comments.

Comments

1

For POST I use something like this:

var resourceLink = string.Format("dbs/{0}/colls/{1}", DataBaseName, DocumentCollectionName);
using (var client = GetClient(
            "POST", 
            resourceLink,
            "docs", 
            PrimaryKey))
{
      try
      {
            var content = 
                    new StringContent("{\"query\":\"SELECT * FROM root\"}", Encoding.UTF8, "application/query+json");
                content.Headers.ContentType.CharSet = "";
            var r = client.PostAsync(
                new Uri(
                    new Uri(EndpointUri), resourceLink + "/docs"), content).Result;
            }
            catch (Exception ex)
            {
            }
        }}

and it works for me

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.