Tag Archives: azure

Azure Table Rest API errors with WinJS application

Published June 7, 2013 11:10 am

If you are using azure SDK to code to its REST API – there are lot of API nuance that is taken care by the SDK. For example – if you are coding nodejs server, or windows store application using c# that uses Azure, nodejs and .net sdk is available. For WinJS, there isn’t azure sdk.

But it shouldn’t be big deal to code directly to the REST API if the basic http lib is available. `WinJS.xhr()` is available to mimics closely what XmlHttpRequest does in the browser. Well, this holds true if the API returns meaningful errors with sufficient contextual details for the error. If that isn’t true, lot of developer(your) time is wasted debugging the error cases. Keep binging to see if other community developer hit similar issue, or parse the http packets sent — for a successful rest api call made by other tool or application – using fiddler or network monitor.

I recently saw two such errors when invoking azure table rest api to query using javascript in WinJS application.

  1. AuthenticationFailed
    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
      <code>AuthenticationFailed</code>
      <message xml:lang="en-US">Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.</message>
    </error>
  2. InvalidHeaderValue
     
    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
      <code>InvalidHeaderValue</code>
      <message xml:lang="en-US">The value for one of the HTTP headers is not in the correct format.</message>
    </error>

AuthenticationFailed was hit when the x-ms-date header was formatted incorrectly. Expected value – Thu, 06 Jun 2013 08:09:50 GMT . Value computed in the code – Thu, 6 Jun 2013 08:20:34 GMT. Note: the difference in ‘0’ prefix for the date. Because of this, the computed stringToSign for the authorization header was incorrect. For details, you can refer here.

InvalidHeaderValue was hit because MaxDataServiceVersion: '2.0;NetFx' header value was missing.

In the first case, if the REST API returned the expected and found ‘stringToSign’ parameter value in the returned error – it would help developer save order of .5 to 1day worth of debugging time. In the second case, if the returned error mentioned the missing header in the error details, again it will save 1-2 hours of debugging time.

Well, you might hit here and cause & solution of the error code for the same api might be different than this – do drop a comment with the cause & solution. If you are a developer coding REST API, do send error codes with sufficient details to save hours of developer time. Thanks for reading so far.

Uploading image to azure blob from windows store app

Published March 22, 2013 11:11 am


msdn gives quick start to upload image file using background transfer.  but I was looking for way to upload image file using WinJS.xhr() in the javascript app.

Windows.Storage.Pickers.FileOpenPicker.pickSingleFileAsync() returns StorageFille object  whereas WinJS.xhr.send() expects a File DOM object. Essentially, what we need is way to get a w3c File DOM object out of StorageFile object. I found finally the method MsApp.createFileFromStorageFile() to do that.

// url needs to be SAS (shared access signature) url for the blob when the container is protected
// otherwise - it requires putting authorization code in the request. I have talked about in previous post.
// but if the javascript is in the client side - you wont like to put your client secret in the app.
// hence, you need to have some mechanism (your service) to return the sas url for your azure container/blob. 

    var url = '';
    var date = new Date().toGMTString().replace('UTC', 'GMT');
    var data = MSApp.createFileFromStorageFile(storageFile);
    var xhrOptions = {
        type: 'PUT',
        url: blobSasUrl,
        headers: {
            'Content-Type': 'image/jpeg',
            'Content-Length': data.size,
            'x-ms-date': date,
            'x-ms-version': '2009-09-19',
            'x-ms-blob-type': 'BlockBlob',
        },
        data: data,
    };
    WinJS.xhr(options).then(null, function onerror(error)
    {
        // handle error
    });

I see that many of us need to upload images to azure blob containers. If you are one of us, it will help to know little bit more to help you better.

Putting a blob in azure from windows store app in javascript

Published March 21, 2013 6:18 pm


I need to put a blob into azure from my windows store app that is being coded in javascript. I did not find azure javascript client lib. Azure does have nodejs sdk but did not find azure javascript client lib. Hence, I coded up my first put blob rest api call. The most tricky part in the code is to generate the authorization shared key. Rest is simply adding properties into XHR call options parameters.

Refer the code below.

var url = 'http://mystorageaccount.blob.core.windows.net/mycontainer/myblob';
                var date = new Date().toGMTString().replace('UTC', 'GMT');
                var xhrOptions = {
                    type: 'PUT', url: url,
                    headers: {
                        'Content-Type': 'application/octet-stream',
                        'Content-Length': contentLength,
                        'x-ms-date': date,
                        'x-ms-version': '2009-09-19',
                        'x-ms-blob-type': 'BlockBlob',
                        data: data
                    }
                };
                var options = { storageAccount: 'mystorageaccount', resourcePath: 'mycontainer/myblob', primaryKey: 'myprimarykey' };
                var authorizationHeaderValue = authorizationHeader.compute(options, xhrOptions);
                xhrOptions.headers.Authorization = authorizationHeaderValue;
                WinJS.xhr(xhrOptions).done(function oncomplete(req)
                {

                }, function onerror(error)
                {
                });
var authorizationHeader = 
{
        /*
        options - refer call to this method
        xhrOptions - parameter for the WinJS.xhr call. computed Authorization header will be used this xhr request only.
        */
        compute: function compute(options, xhrOptions)
        {
            var sig = this._computeSignature(options, xhrOptions);
            var result = 'SharedKey ' + options.storageAccount + ':' + sig;
            return result;
        },
        _computeSignature: function computeSignature(options, xhrOptions)
        {
            var sigString = this._getSignatureString(options, xhrOptions);
            var key = CryptoJS.enc.Base64.parse(options.primaryKey);
            var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
            hmac.update(sigString);
            var hash = hmac.finalize();
            var result = hash.toString(CryptoJS.enc.Base64);
            return result;
        },
        _getHeaderOrDefault: function getHeaderOrDefault(headers, headerName)
        {
            var result = headers[headerName];
            result = result ? result : '';
            return result;
        },
        _getSignatureString: function createSignatureString(options, xhrOptions)
        {
            var headers = xhrOptions.headers;
            var httpVerb = xhrOptions.type.toUpperCase();
            var sigItems = [];
            sigItems.push(httpVerb);
            var contentEncoding = this._getHeaderOrDefault(headers, 'Content-Encoding');
            sigItems.push(contentEncoding);
            var contentLanguage = this._getHeaderOrDefault(headers, 'Content-Language');
            sigItems.push(contentLanguage);
            var contentLength = this._getHeaderOrDefault(headers, 'Content-Length');
            sigItems.push(contentLength);
            var contentMD5 = this._getHeaderOrDefault(headers, 'Content-MD5');
            sigItems.push(contentMD5);
            var contentType = this._getHeaderOrDefault(headers, 'Content-Type');
            sigItems.push(contentType);
            var date = this._getHeaderOrDefault(headers, 'Date');
            sigItems.push(date);
            var ifModifiedSince = this._getHeaderOrDefault(headers, 'If-Modified-Since');
            sigItems.push(ifModifiedSince);
            var ifMatch = this._getHeaderOrDefault(headers, 'If-Match');
            sigItems.push(ifMatch);
            var ifNoneMatch = this._getHeaderOrDefault(headers, 'If-None-Match');
            sigItems.push(ifNoneMatch);
            var ifUnmodifiedSince = this._getHeaderOrDefault(headers, 'If-Unmodified-Since');
            sigItems.push(ifUnmodifiedSince);
            var range = this._getHeaderOrDefault(headers, 'Range');
            sigItems.push(range);
            var canonicalizedHeadersString = this._getCanonicalizedHeadersString(xhrOptions);
            sigItems.push(canonicalizedHeadersString);
            var canonicalizedResource = this._getCanonicalizedResource(options);
            sigItems.push(canonicalizedResource);

            var result = sigItems.join('\n');
            return result;
        },
        _getCanonicalizedHeadersString: function getCanonicalizedHeadersString(xhrOptions)
        {
            var headers = xhrOptions.headers;
            var headerNames = Object.keys(headers);
            var lowercaseHeaderNames = headerNames.map(function tolower(name)
            {
                return name.toLowerCase();
            });
            var msHeaderNames = lowercaseHeaderNames.filter(function isMsHeader(name)
            {
                if (name.indexOf('x-ms-') == 0)
                    return true;
                else
                    return false;
            });
            msHeaderNames.sort();
            var strItems = [];
            for (var i = 0; i < msHeaderNames.length; i++)
            {
                var key = msHeaderNames[i];
                var value = headers[key] || '';
                value = value.trim();
                var item = key + ':' + value;
                strItems.push(item);
            }

            var result = strItems.join('\n');
            return result;
        },
        _getCanonicalizedResource: function getCanonicalizedResource(options)
        {
            var items = [];
            var path = "/" + options.storageAccount;
            path += "/" + decodeURIComponent(options.resourcePath);
            items.push(path);
            // TODO: handle storage rest api query parameters. 
            // need to add them to options and items 
            // as described here - http://msdn.microsoft.com/en-us/library/windowsazure/dd179428.aspx#Constructing_Element
            var result = items.join('\n');
            return result;
        }
    }

Notes

  1. Code uses the google code crypto lib –  – need to download the zip, extract and use rollups\hmac-sha256.js and components\enc-base64-min.js from the zip file.
  2. CryptoJS.algo.HMAC.create expects the key to be in utf8 or wordskey. Hence, the azure base64key needs to be decoded prior to passing to the call. Thanks to iug to give lead in this.
  3. hmac.update(message) call encodes the message string using utf8. hence, we are good there.