Azure Function Access CloudBlobContainer from HttpTriggerFunction - c#

Very, very new to Azure Functions and getting very frustrated.
All I want to do is execute on a 'get' request from a HttpTriggerFunction and return stream content from the CloudBlobContainer.
I really don't see why this is so hard. Just trying to host a SPA using Azure Functions.
Something like this
public static class UIHandler
{
[FunctionName("UIHandler")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequest req,
TraceWriter log,
CloudBlobContainer container)
{
log.Info("C# HTTP trigger function processed a request.");
var stream = await container.GetBlockBlobReference({Infer file name from request here}).OpenReadAsync();
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StreamContent(stream)
};
}
}
When I try to run this I get the following error.
Run: Microsoft.Azure.WebJobs.Host: Error indexing method
'UIHandler.Run'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter
'container' to type CloudBlobContainer. Make sure the parameter Type
is supported by the binding. If you're using binding extensions (e.g.
ServiceBus, Timers, etc.) make sure you've called the registration
method for the extension(s) in your startup code (e.g.
config.UseServiceBus(), config.UseTimers(), etc.).
I'm using Azure Functions 2. I can't see from the web how to setup the browsing extensions for this. Iv'e also looked into Input and Output bindings. I don't understand what makes a parameter input or output bound when your using C# that only seems to exist in the JSON.
Do I need to corresponding JSON file ? If so what is it called where does it go.
Thanks in Advance

Have a look at Blob Storage Input Binding. The very first sample there shows how to read blob stream, just replace Queue Trigger with HTTP trigger, e.g.
[FunctionName("UIHandler")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "{name}")] HttpRequest req,
string name,
TraceWriter log,
[Blob("samples-workitems/{name}", FileAccess.Read)] Stream stream)
{
log.Info($"C# HTTP trigger function processed a request for {name}.");
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StreamContent(stream)
};
}

Related

Azure function does not accept XML, v4 net6.0

I am trying to create an azure function that will receive XML files and process them accordingly. I managed to achieve this on my local environment and everything works as intended but when I deployed it to Azure and testing, it comes back with the following 500 internal server error:
Data at the root level is invalid. Line 1, position 1.
Is the exact same xml that works on the local environment, I checked it on the online validators and on visual studio, there is no problem with the xml. Hoovering over the text brings:
Only content-type of application/json is accepted.
I have tried different approaches in code to go around reading the string but with no success:
public class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
try
{
log.LogInformation("C# HTTP trigger function processed a request.");
//string msg = await new StreamReader(req.Body).ReadToEndAsync();
XmlDocument xmlDocumnet = new XmlDocument();
xmlDocumnet.LoadXml(await new StreamReader(req.Body).ReadToEndAsync());
and
string msg = await new StreamReader(req.Body).ReadToEndAsync();
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(msg);
writer.Flush();
stream.Position = 0;
xmlDocumnet.Load(stream);
}
I tried to add a header to specify content-type : text/xml but it comes back with a 500:
Only content-type of application/json is accepted.
What would be the best approach in this case? I was thinking to create an intermediary storage which I then would load the file in the function without passing it into the body but that would increase the processing time.

Error: Cannot convert from Microsoft.AspNetCore.Http.IFormFile to System.IO.Stream

I'm trying to create Azure function which takes image file from html form POST request and saves it to Blob Storage for further usage with another function. Here is my code:
public static class Function2
{
[FunctionName("Function2")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
foreach (var file in req.Form.Files)
{
using (var ms = new MemoryStream())
{
var file2 = req.Form.Files[0];
await file2.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
var connectionString = "DefaultEndpointsProtocol=https;" +
"AccountName=mystorageaccount;" +
"AccountKey=8Hk5k6j65j5j665j67k==;" +
"EndpointSuffix=core.windows.net";
// intialize BobClient
Azure.Storage.Blobs.BlobClient blobClient = new Azure.Storage.Blobs.BlobClient(
connectionString: connectionString,
blobContainerName: "image-storage",
blobName: "images");
// upload the file
blobClient.Upload(file2);
}
}
return new OkResult("Image uploaded successfully");
}
}
However this raises exception:
Error CS1503 Argument 1: cannot convert from
'Microsoft.AspNetCore.Http.IFormFile' to 'System.IO.Stream'
Any advise would be very highly appreciated.
Edit: I have previously created Blob Container "image-storage" to my storage account using Azure Portal.
BlobCients Upload method expects a Stream instead of an IFormFile.
Passing the MemoryStream you have in the ms variable will resolve the issue.
blobClient.Upload(ms);

Display files from Azure Blob Storage by Azure function

I'm trying to get files and display them in my browser from Azure Blob Storage via an Azure function. I could manage to download the files when I navigate to the url but I couldn't display them as a static file/image in my browser.
I just want to display it in browser rather than downloading.
I've tried some sdk command but it didn't work. Here's what I've tried:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
ILogger log)
{
var cloudStorageAccount =
CloudStorageAccount.Parse(AzureStorageConnectionString);
var cloudBlobClient =
cloudStorageAccount.CreateCloudBlobClient();
var cloudBlobContainer =
cloudBlobClient.GetContainerReference(
AzureStorageFilePath);
await cloudBlobContainer.CreateIfNotExistsAsync();
var blobName =
req.Query["name"];
var cloudBlockBlob =
cloudBlobContainer.GetBlockBlobReference(blobName);
var ms = new MemoryStream();
await cloudBlockBlob.DownloadToStreamAsync(ms);
return new FileContentResult(ms.ToArray(), cloudBlockBlob.Properties.ContentType);
}
Any ideas would be appreciated. Thanks!
Kindly check the content type of the file(s) which you want to display ,if the content type is "application/octet-stream" it will cause the file to download.
By default if content-type is not supplied azure sdk sets it as "application/octet-stream" which causes the file to download,set the correct content-type for the file ex :- for image it should be "image/jpeg".
Hopefully this should fix the issue.

How to do a durable post call in an ServiceBusTrigger azure function?

Hey I'm using a ServiceBusTrigger azure function to get the messages received in a queue, and then send them to my webapi which is going to do some stuff with that content
[FunctionName("MyAzureFunction")]
public async void Run(
[ServiceBusTrigger("<MyQueue>", Connection = "<MyConnectionString>")] Message myQueueItem, ILogger log)
{
log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem.ToString()}");
var client = new HttpClient();
// Retrieving the string content from the message
var bodyMessage = Encoding.UTF8.GetString(myQueueItem.Body);
// Calling my API to do something based on the message content
var response = await client.PostAsync("<MyAPIUrl>", new StringContent(bodyMessage, Encoding.UTF8, "application/json"));
// doing something based on the response
}
I've been reading about azure functions and in order to it gets cheaper I read about durable functions, I'm looking forward how to use them so I can take decisions based on my response and I can get it working with this ServiceBusTrigger
Needs to be changed the current ServiceBusTrigger function so it calls another Function that will actually do the job:
[FunctionName("MyAzureFunction")] public async void Run(
[ServiceBusTrigger("<MyQueue>", Connection = "<MyConnectionString>")] Message myQueueItem,
[DurableClient] IDurableOrchestrationClient orchestratorClient,
ILogger log) {
log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem.ToString()}");
// Here is where you need to specify in the first parameter the name of the function to be called
// and the last parameter are the params you'll send to that one
var instanceId = await orchestratorClient.StartNewAsync("MyPostFunction", null, myQueueItem);
log.LogInformation($"C# ServiceBus queue trigger function created an async instance of 'MyPostFunction' with the ID: {instanceId}");
}
Then is needed to create another function that will be OrchestrationTrigger type, that will look like this:
[FunctionName("MyPostFunction")] public async void RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context) {
// using the context can be retrieved the parammeters passed in the function above
// in this case I just specify the type of that one and that's it
var myQueueItem = context.GetInput<Message>();
var bodyMessage = Encoding.UTF8.GetString(myQueueItem.Body);
// Create a URI of your API url
var postUri = new Uri($"<MyAPIUrl>");
// depending on your WebAPI you'll need to specify the content type in the headers
var headers = new Dictionary<string, StringValues>() { { "Content-Type", "application/json" } };
// creating durable http request
var request = new DurableHttpRequest(HttpMethod.Post, postUri, headers, bodyMessage);
// Doing the http call async, in this context you'll save money since your function will not be completely waiting for a response
// this one will keep just checking to see if there's a response available or not
var response = await context.CallHttpAsync(request);
// do your stuffs depending in the response
}
In my case I had to specicify the headers in the request, otherwise I used to get 415 Unsupported Media Type can be done in that way, or just creating the request without specifying any header at the begining and then adding those like this:
var request = new DurableHttpRequest(HttpMethod.Post, postUri, null, bodyMessage);
request.Headers.Add("Content-Type", "application/json");
Both options work

Blob binding in httpTrigger not working in VS2017 Azure function template

I want to pass the filename of blob to the httptrigger, through get request as below.
http://localhost:7071/api/CSVDataMigrationHttpTrigger/testdata.csv
Code for the azure function
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "CSVDataMigrationHttpTrigger/{name}")]
HttpRequest req, string name,
[Blob("csvdata-upload/{name}", FileAccess.Read, Connection = "AzureWebJobsStorage")]
Stream inputBlob, ILogger log)
{}
inputBlob parameter is not resolved and it returns null.
But if i give filename as "testData.csv" as below in the Blob parameter, then inputBlob get resolved properly.
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "CSVDataMigrationHttpTrigger/{name}")]
HttpRequest req, string name,
[Blob("csvdata-upload/testData.csv", FileAccess.Read, Connection = "AzureWebJobsStorage")]
Stream inputBlob, ILogger log){}
I found out finally, the filename was case sensitive, when passed to blob. Hope it helps anyone who has the same issue.
Check whether your blob is actually uploaded in the Storage Container. The stream will be null only if the blob is Not Exist/Unable to find in the container

Categories

Resources