0

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.

2
  • 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. Commented Apr 11, 2017 at 18:12
  • I tried implementing the ExtendedInformation as mentioned here link, but there is no extended information for my error. Just 400 - Bad Request. Commented Apr 11, 2017 at 20:45

1 Answer 1

1

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...
}
Sign up to request clarification or add additional context in comments.

8 Comments

When I implement your suggestion with the binder, I get this error: error CS1503: Argument 1: cannot convert from 'System.Attribute[]' to 'System.Attribute'
Changed IBinder to Binder
That worked to fix the Attribute issue. Now I get error: 'string': type used in a using statement must be implicitly convertible to 'System.IDisposable'
Replace using block with normal assignment
Yes, a connection name (not connection string) that would be in "connection" parameter in function.json in case of declarative binding
|

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.