0
votes

I am having trouble reading text stored in a blob on Azure (Blob) Storage.

The blob contains only a single line of text (a string). The blob is filled with text via an Azure Functions HttpTrigger (C#) that receives text via a POST and saves the text to a blob with a user-specified name. The name is converted to all lowercase when saving the blob.

The user can then visit a simple HTML webpage and enter the blob's name into a form. When the user clicks "Submit" on the HTML form, a POST is performed against a different Azure Function API. This function accesses the blob and reads text from it. Whenever I test the Function from within Azure Functions or using Postman, it works correctly (I get the blob text back).

When I try to use the webpage to interact with the API, I get a "400 - Bad Request" when the Azure Function goes to read from the blob. Please see the details below:

Log of API Working correctly from Postman:

2017-04-11T20:19:14.340 Function started (Id=ea82f5c6-4345-40cc-90a5-1cb1cad78b7b)

2017-04-11T20:19:14.340 C# HTTP trigger function processed a request.

2017-04-11T20:19:14.340 Data from POST: blobName=TestBlob1submit=SubmitButtonText

2017-04-11T20:19:14.340 Blob name is: testblob1

2017-04-11T20:19:14.340 Accessing Azure Storage account.

2017-04-11T20:19:14.402 Text in Blob: Hello world test!

2017-04-11T20:19:14.402 Function completed (Success, Id=ea82f5c6-4345-40cc-90a5-1cb1cad78b7b)

Log of API Not Working via HTML Form:

2017-04-11T20:19:52.594 Function started (Id=1b1a39b6-0ab8-4673-bbf0-ae0006f7f7cf)

2017-04-11T20:19:52.594 C# HTTP trigger function processed a request.

2017-04-11T20:19:52.594 Data from POST: blobName=TestBlob1

submit=Retrieve Blob Text

2017-04-11T20:19:52.594 Blob name is: testblob1

2017-04-11T20:19:52.594 Accessing Azure Storage account.

2017-04-11T20:19:52.626 Function completed (Failure, Id=1b1a39b6-0ab8-4673-bbf0-ae0006f7f7cf)

2017-04-11T20:19:52.672 Exception while executing function: Functions.Austin-SteelThread-HttpTrigger-DisplayBlobText. Microsoft.WindowsAzure.Storage: The remote server returned an error: (400) Bad Request.

Here is the Azure Function in question:

#r "Microsoft.WindowsAzure.Storage"
using System;
using System.IO;
using System.Net;
using System.Text;
using Microsoft.Azure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log, Binder binder)
{
    log.Info("C# HTTP trigger function processed a request.");

    // Get text passed in POST
    string postData = await req.Content.ReadAsStringAsync();
    log.Info("Data from POST: " + postData);

    // Format blobName string to remove unwanted text
    // Help from http://stackoverflow.com/questions/9505400/extract-part-of-a-string-between-point-a-and-b
    int startPos = postData.LastIndexOf("blobName=") + "blobName=".Length;
    int length = postData.IndexOf("submit=") - startPos;
    string blobName = postData.Substring(startPos, length);
    blobName = blobName.ToLower();      // Name of blob must be all lower-case

    log.Info("Blob name is: " + blobName);

    // START BLOB READING
    log.Info("Accessing Azure Storage account.");
    string containerAndBlob = "usertext-container/blob-" + blobName;

    var attributes = new Attribute[]
    {
         new StorageAccountAttribute("[StorageAccountName]"),
         new BlobAttribute(containerAndBlob)
    };

    try
    {
        userBlobText = await binder.BindAsync<string>(attributes);
    }
    catch (StorageException ex)
    {
        var requestInformation = ex.RequestInformation;
        var extendedInformation = requestInformation.ExtendedErrorInformation;

        if (extendedInformation == null)
        {
            log.Info("No Extended Error Information!");
            log.Info(requestInformation.HttpStatusMessage);
        }
        else
        {
            log.Info(requestInformation.HttpStatusMessage);

            var errorMessage = string.Format("({0}) {1}", extendedInformation.ErrorCode, extendedInformation.ErrorMessage);

            var errorDetails = extendedInformation.AdditionalDetails.Aggregate("", (s, pair) =>
            {
                return s + string.Format("{0}={1},", pair.Key, pair.Value);
            });

            log.Info(errorMessage + ": Error Details: " + errorDetails);
        }
    }

    log.Info("Text in Blob: " + userBlobText.ToString());
    // END BLOB READING

    return userBlobText == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass blob name in the request body.")
        : req.CreateResponse(HttpStatusCode.OK, "Your blob stored the text: " + userBlobText.ToString());
}

How can I fix this issue so that the Function reads the blob text and the web browser displays the blob's text (right now I get just an empty string)? Thank you in advance.

1
You should actually cast your exception as StorageException. Then you'll be able to see more details about the 400 error. I would recommend looking into RequestInformation property of the exception. HTH.Gaurav Mantri
I tried implementing the ExtendedInformation as mentioned here link, but there is no extended information for my error. Just 400 - Bad Request.TechAust10

1 Answers

1
votes

Instead of connecting to Blob storage manually, you should take advantage of the binding engine. Add binder parameter to your function and then use it to retrieve the file:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, 
    TraceWriter log, Binder binder)
{
    // Do await, not .Result
    string postData = await req.Content.ReadAsStringAsync();

    // ... get your HTTP parameters here

    var attributes = new Attribute[]
    {
         new StorageAccountAttribute(accountName),
         new BlobAttribute(blobName) // blobName should have "container/blob" format
    };

    var userBlobText = await binder.BindAsync<string>(attributes);
    // do whatever you want with this blob...
}