1
votes

Following code is aimed at deleting a row from table with given partition key and rowkey. But I get following request/response in fiddler. How can I correct the error?

Request

DELETE https://hireazurestorageacct.table.core.windows.net/mytable(PartitionKey='sample1',%20RowKey='0001')?timeout=20 HTTP/1.1 Accept: application/json;odata=nometadata x-ms-date: Mon, 08 May 2017 17:59:14 GMT x-ms-version: 2015-04-05 Accept-Charset: UTF-8 MaxDataServiceVersion: 3.0;NetFx DataServiceVersion: 1.0;NetFx If-Match: * Content-Type: application/json Authorization: SharedKeyLite hireazurestorageacct:3ZHX8lYBec+/9ytiNQb+JV5dpFkLAieuwB5veMkLVUU= Host: hireazurestorageacct.table.core.windows.net

Response

HTTP/1.1 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. Content-Length: 299 Content-Type: application/json Server: Microsoft-HTTPAPI/2.0 x-ms-request-id: a9244f7f-0002-0048-0824-c8afc5000000 Date: Mon, 08 May 2017 17:59:14 GMT

{"odata.error":{"code":"AuthenticationFailed","message":{"lang":"en-US","value":"Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:a9244f7f-0002-0048-0824-c8afc5000000\nTime:2017-05-08T17:59:14.9335100Z"}}}

Code

public static int DeleteEntity(string storageAccount, string accessKey, string tableName, string partitionkey, string rowkey) { string uri = $@"https://{storageAccount}.table.core.windows.net/{tableName}(PartitionKey='{partitionkey}', RowKey='{rowkey}')?timeout=20"; string resource = $@"{tableName}";

        // Web request 
        var request = (HttpWebRequest)WebRequest.Create(uri);
        request.Method = "DELETE";
        request.Accept = "application/json;odata=nometadata";
        request.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
        request.Headers.Add("x-ms-version", "2015-04-05");
        request.Headers.Add("Accept-Charset", "UTF-8");
        request.Headers.Add("MaxDataServiceVersion", "3.0;NetFx");
        request.Headers.Add("DataServiceVersion", "1.0;NetFx");
        request.Headers.Add("If-Match", "*");
        request.ContentType = "application/json";


        // Signature string for  Shared Key Lite Authentication must be in the form
        // StringToSign = Date + "\n" + CanonicalizedResource
        // Date 
        string stringToSign = request.Headers["x-ms-date"] + "\n";

        // Canonicalized Resource in the format  /{0}/{1} where 0 is name of the account and 1 is resources URI path
        stringToSign += "/" + storageAccount + "/" + resource;

        // Hash-based Message Authentication Code (HMAC) using SHA256 hash
        var hasher = new HMACSHA256(Convert.FromBase64String(accessKey));

        // Authorization header
        string strAuthorization = "SharedKeyLite " + storageAccount + ":" + Convert.ToBase64String(hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign)));

        // Add the Authorization header to the request
        request.Headers.Add("Authorization", strAuthorization);
        Thread.Sleep(1000);
        // Execute the request
        try
        {
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                using (var r = new StreamReader(response.GetResponseStream()))
                {
                    return (int)response.StatusCode;
                }
            }
        }
        catch (WebException ex)
        {
            // get the message from the exception response
            using (var sr = new StreamReader(ex.Response.GetResponseStream()))
            {
                var res = sr.ReadToEnd();
                // Log res if required
            }

            return (int)ex.Status;
        }
    }
2

2 Answers

0
votes

Status 403 means you need to authenticate to access a resource. Any decent server will not give you anything that includes any information about the resource. So you will get the same reply, whether the resource is there or not.

0
votes

According to your codes, I guess there are something wrong with your authorization token when you use the resource to generate the authorization token.

I suggest you could try below codes to delete the table entity.

Call method:

    AzureTableHelper.DeleteEntity("{storageaccount}", "{accesskey}", "{tablename}", "{partitionkey}", "{rowkey}" );

Delete method:

 public static int DeleteEntity(string storageAccount, string accessKey, string tableName, string partitionkey, string rowkey)
        {

            string host = string.Format(@"https://{0}.table.core.windows.net/", storageAccount);
            string resource = string.Format(@"{0}", tableName) + string.Format("(PartitionKey='{0}',RowKey='{1}')", partitionkey, rowkey);

            string uri = host + resource;

            //if you want to check the etag you need firstly get the etag then delete the entity 
            //string jsonData = "";
            //int responseCode = RequestResource(
            //    storageAccount,
            //    accessKey,
            //    resource,
            //    out jsonData);
            //var jsonObject = JObject.Parse(jsonData);

            //string time = jsonObject.GetValue("odata.etag").ToString();

            //string time = obj.Timestamp;
            // Web request 
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
            request.Method = "DELETE";
            request.ContentType = "application/json";    
            request.Accept = "application/json;odata=nometadata";
            request.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
            request.Headers.Add("x-ms-version", "2015-04-05");
            request.Headers.Add("If-Match", "*");
            request.Headers.Add("Accept-Charset", "UTF-8");
            request.Headers.Add("MaxDataServiceVersion", "3.0;NetFx");
            request.Headers.Add("DataServiceVersion", "1.0;NetFx");


            // Signature string for  Shared Key Lite Authentication must be in the form
            // StringToSign = Date + "\n" + CanonicalizedResource
            // Date 
            string stringToSign = request.Headers["x-ms-date"] + "\n";

            // Canonicalized Resource in the format  /{0}/{1} where 0 is name of the account and 1 is resources URI path
            stringToSign += "/" + storageAccount + "/" + resource;

            // Hash-based Message Authentication Code (HMAC) using SHA256 hash
            System.Security.Cryptography.HMACSHA256 hasher = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(accessKey));

            // Authorization header
            string strAuthorization = "SharedKeyLite " + storageAccount + ":" + System.Convert.ToBase64String(hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign)));

            // Add the Authorization header to the request
            request.Headers.Add("Authorization", strAuthorization);

            // Execute the request
            try
            {
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    using (System.IO.StreamReader r = new System.IO.StreamReader(response.GetResponseStream()))
                    {
                        string jsonResponse = r.ReadToEnd();
                        return (int)response.StatusCode;
                    }
                }
            }
            catch (WebException ex)
            {
                // get the message from the exception response
                using (System.IO.StreamReader sr = new System.IO.StreamReader(ex.Response.GetResponseStream()))
                {
                    string res = sr.ReadToEnd();
                    // Log res if required
                }

                return (int)ex.Status;
            }
        }