Azure Web Function: Create binding from http request to inputBlob path - azure

Desired Scenario
From an Arduino:
take a photo and upload image as a blob in Azure storage container (works fine)
call a Web Function using HTTP with blob name and other info (works fine)
From the Web function
read the HTTP request (works fine)
read the blob using the information from the HTTP request (does not work)
process the blob (not implemented yet)
respond result to Arduino
Problem
I can't figure out how to make the binding from the path to the HTTP parameters.
Web Function Config
{
"bindings": [
{
"authLevel": "function",
"name": "request",
"type": "httpTrigger",
"direction": "in",
"methods": [
"post"
]
},
{
"name": "$return",
"type": "http",
"direction": "out"
}
],
"disabled": false
}
Error:
Function ($HttpTriggerCSharp1) Error: Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.HttpTriggerCSharp1'. Microsoft.Azure.WebJobs.Host: No binding parameter exists for 'blobName'.
ORIGINAL, NON-WORKING code (working code below):
using System.Net;
public static async Task<HttpResponseMessage> Run(
HttpRequestMessage request,
string blobName, // DOES NOT WORK but my best guess so far
string inputBlob,
TraceWriter log)
{
// parse query parameter
string msgType = request.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "msgType", true) == 0)
.Value;
// Get request body
dynamic data = await request.Content.ReadAsAsync<object>();
// Set name to query string or body data
msgType = msgType ?? data?.msgType;
string deviceId = data.deviceId;
string blobName = data.BlobName; // DOES NOT COMPILE
log.Info("blobName=" + blobName); // DOES NOT WORK
log.Info("msgType=" + msgType);
log.Info("data=" + data);
return msgType == null
? request.CreateResponse(HttpStatusCode.BadRequest, "HTTP parameter must contain msgType=<type> on the query string or in the request body")
: request.CreateResponse(HttpStatusCode.OK, "Hello " + deviceId + " inputBlob:");// + inputBlob );
}
HTTP request looks like this:
https://xxxxxxprocessimagea.azurewebsites.net/api/HttpTriggerCSharp1?code=CjsO/EzhtUBMgRosqxxxxxxxxxxxxxxxxxxxxxxx0tBBqaiXNewn5A==&msgType=New image
"deviceId": "ArduinoD1_001",
"msgType": "New image",
"MessageId": "12345",
"UTC": "2017-01-08T10:45:09",
"FullBlobName": "/xxxxxxcontainer/ArduinoD1_001/test.jpg",
"BlobName": "test.jpg",
"BlobSize": 9567,
"WiFiconnects": 1,
"ESPmemory": 7824,
"Counter": 1
(I know, msgType appears both in URL and in headers. I've tried different combinations - no effect).
If what I'm trying to do is impossible, alternative suggestions are also welcome. I just need a way through.
This code works thanks to Tom Sun's hint. The trick was to remove the binding to the storage blob in the JSON and instead just call the blob directly from the code.
#r "Microsoft.WindowsAzure.Storage"
using Microsoft.WindowsAzure.Storage; // Namespace for CloudStorageAccount
using Microsoft.WindowsAzure.Storage.Blob; // Namespace for Blob storage types
using Microsoft.WindowsAzure.Storage.Queue;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using System.Net;
using System.IO;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage request, string inputBlob, TraceWriter log)
{
string msgType = request.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "msgType", true) == 0)
.Value;
dynamic data = await request.Content.ReadAsAsync<object>();
msgType = msgType ?? data?.msgType;
string deviceId = data.deviceId;
string blobName = data.BlobName;
string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("nnriothubcontainer/" + deviceId);
CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
MemoryStream imageData = new MemoryStream();
await blob.DownloadToStreamAsync(imageData);
return msgType == null
? request.CreateResponse(HttpStatusCode.BadRequest, "HTTP parameter must contain msgType=<type> on the query string or in the request body")
: request.CreateResponse(HttpStatusCode.OK, "Hello " + deviceId + " inputBlob size:" + imageData.Length);// + inputBlob );
}

take a photo and upload image as a blob in Azure storage container (works fine)
call a Web Function using HTTP with blob name and other info (works fine) From the Web function
read the HTTP request (works fine)
Base on my understanding, you can read the Http info including blob uri ,blob name, and try to operate the storage blob in the Azure Http Trigger Function.
If it is that case, we could try to refer to "Microsoft.WindowsAzure.Storage" and import namespaces. Then we could operate Azure storage with Azure storeage SDK. More detail info about how to operate storage blob please refer to document.
#r "Microsoft.WindowsAzure.Storage"
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;

Related

how can we use await blob.UploadAsync(data, blobHttpHeader); to accept a json request

I have been trying to send a json data through http trigger function using c# code to store this data into azure blob storage.
For ex:
{"name1":"Chandra"} is the data i want to push it as an object entry into azure blob using a Http trigger function. And below is the code i have written to push the JSON request object into the azure blob:
using System;
using System.IO;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Specialized;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Text;
using Azure.Storage.Blobs.Models;
namespace fileuploadfunc
{
public static class FileUpload
{
[FunctionName("FileUpload")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("File Upload script loading started");
string Connection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
string containerName = Environment.GetEnvironmentVariable("ContainerName");
var blobClient = new BlobContainerClient(Connection, containerName);
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
log.LogInformation("requestbody",requestBody);
//Stream myBlob = data;
//var file = req.Form.Files["File"];
int i = +1;
var random = i;
// var blobClient1 = new SpecializedBlobClientOptions();
// var blob = blobClient.GetBlobClient(file.FileName);
var blobHttpHeader = new BlobHttpHeaders();
blobHttpHeader.ContentType = "application/json";
var blob = blobClient.GetBlobClient("events1");
var uploadedBlob = await blob.UploadAsync(data, blobHttpHeader);
//await blobClient.UploadBlobAsync("event_json",data,cancellationToken:data);
//blob.Upload(myBlob);
// blob.Upload(json);
return new OkObjectResult("file uploaded successfully");
}
}
}
This code is not working as i expected
could you please help me with your suggestions on what should be added in the code in-order to accept json type of request and push it as a object into azure blob.
The BlobClient.UploadAsync method can only upload String, Binary and Stream data.
If you want to upload JSON data into Blob. you can convert your JSON Data into formats like (String, Binary and Stream)
Follow the below ways to achieve
Using Stream
Better way is you can use the MemoryStream to add the JSON values into the blob.
# using Memory Stream
using (MemoryStream mstream = new MemoryStream(Encoding.UTF8.GetBytes(<Your Json data>)))
{
# Upload the json stream into blob
await blob.UploadAsync(mstream);
}
Using String
Convert your JSON into the string and upload it into the blob
await blob.UploadAsync(<Your JSON Converted String>);
Refer here for more information

HTTPTrigger Azure Function with CosmosDb input is not working after deployment

I have created below HttpTrigger function which takes CosmosDb item as input. This code works perfectly fine on when run locally. I am using Visual Studio Code to create function and deploying to Azure after successfully ran locally. But it doesn't work on Azure, it doesn't give error or it seems it not getting any response.
public static class HttpTriggerWithCosmosDb
{
[FunctionName("HttpTriggerWithCosmosDb")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "HttpTriggerWithCosmosDb/{id}")] HttpRequest req,
[CosmosDB(
databaseName : "func-io-learn-db",
collectionName : "Bookmarks",
ConnectionStringSetting = "CosmosDBConnection",
Id = "{id}",
PartitionKey = "{id}"
)] BookMarkItem item,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = item.Url;
Console.Write( item.Id);
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
While publishing, in output I see below message:
HttpTriggerCSharp1:
https://nps-func.azurewebsites.net/api/HttpTriggerCSharp1
TestMessage: https://nps-func.azurewebsites.net/api/TestMessage
5:33:02 PM nps-func: WARNING: Some http trigger urls cannot be
displayed in the output window because they require an authentication
token.
It is not showing This above function in response.
Update:
Full code can be found on Git
I got the issue, what is happening here. I had to manually add CosmosDBConnection to Azure with Azure Function:Add New Settings... command before I publish the function.
I am new to function, I was not aware this is how app_settings need to be added.

Azure functions http trigger Unity3d 2019.3

I've created a test HTTP Trigger azure function. I can trigger it correctly on azure portal and browser. However trigger from Unity editor gives "Error HTTP/1.1 500 Internal Server Error".
starting Azure function:
public static async Task<IActionResult> Run(HttpRequest req, ILogger log){
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");}
My Unity code:
formData.Add(new MultipartFormDataSection("name", "SampleName", "text/plain"));
UnityWebRequest www = UnityWebRequest.Post("https://samplefunction.azurewebsites.net/api/HttpTriggerTest?herelongblob", formData);
yield return www.SendWebRequest();
Azure CORS configuration: Request Credentials ON: Enable Access-Control-Allow-Credentials.
Function is setup always on. Integrate-Trigger: selected methods GET, POST. Authorisation level:Function.
function's host.json: "version": "2.0", "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[1.*, 2.0.0)"
App Service authentication:Anonymous
Unity-Azure sdk and google setup search results seems all outdated/not supported :(
What route should I take to get this to work please? happy to try any sdk / unity asset store to reach azure you may suggest! Cheers!
The error occurs in the two lines below in your code:
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
Since you set text/plain in your code. So when you do the www.SendWebRequest(), it will send a form data but not a json format data. So it will show the error message.
Below I provide the code for your reference:
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var arr = req.Form.Keys;
foreach (var key in arr) {
log.LogInformation(key);
log.LogInformation(req.Form[key]);
}
return (ActionResult)new OkObjectResult("success");
}
You can get the key and the value of the form data in the code above and then compose them to json format and then do DeserializeObject. Or if you want to use the data to create a object, you can even do not do DeserializeObject and just set the value into your object directly.
By the way, you can also try to change the text/plain to application/json in your code new MultipartFormDataSection("name", "SampleName", "text/plain"). But, as I don't know much about MultipartFormDataSection, so I'm not sure if this solution can work.
Hope it helps.

How to use Azure Function App Function Tester (Example)

Could someone provide and example of how to use the Azure Function App Function Tester with an Example?
This is some default sample code for a webhook function:
#r "Newtonsoft.Json"
using System;
using System.Net;
using Newtonsoft.Json;
public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info($"Webhook was triggered!");
string jsonContent = await req.Content.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(jsonContent);
if (data.first == null || data.last == null)
{
return req.CreateResponse(HttpStatusCode.BadRequest, new
{
error = "Please pass first/last properties in the input object"
});
}
return req.CreateResponse(HttpStatusCode.OK, new
{
greeting = $"Hello {data.first} {data.last}!"
});
}
This is an image of the Function App Function Tester in MS Azure
In the Tester blade, you could set the HTTP method, header, parameter,etc.
For the sample code you gave, you should provide Request body like a JSON format, then click the Run button.
{
"first": "Azure",
"last": "Functions"
}
Then you could find the function log.
Update:
If you want to use different http methods,
you could go to Integrate of the function, then check the Selected HTTP methods option.
you could set the methods in the function.json file.
For example:
"methods": [
"get",
"post",
"delete",
"head"
]

Check if file exists on blob storage with Azure functions

Based on https://ppolyzos.com/2016/12/30/resize-images-using-azure-functions/ I have the following C# code to resize an image using Azure Functions.
#r "Microsoft.WindowsAzure.Storage"
using ImageResizer;
using ImageResizer.ExtensionMethods;
using Microsoft.WindowsAzure.Storage.Blob;
public static void Run(Stream inputBlob, string blobname, string blobextension, CloudBlockBlob outputBlob, TraceWriter log)
{
log.Info($"Resize function triggered\n Image name:{blobname} \n Size: {inputBlob.Length} Bytes");
log.Info("Processing 520x245");
/// Defining parameters for the Resizer plugin
var instructions = new Instructions
{
Width = 520,
Height = 245,
Mode = FitMode.Carve,
Scale = ScaleMode.Both
};
/// Resizing IMG
Stream stream = new MemoryStream();
ImageBuilder.Current.Build(new ImageJob(inputBlob, stream, instructions));
stream.Seek(0, SeekOrigin.Begin);
/// Changing the ContentType (MIME) for the resulting images
string contentType = $"image/{blobextension}";
outputBlob.Properties.ContentType = contentType;
outputBlob.UploadFromStream(stream);
}
The result will be an image named 520x245-{blobname}.{blobextension}.
I would like the code to run only if the resulting image does not already exist in the blob container.
How can I get the existing files on the container?
Since you are using CloudBlockBlob type to bind outputBlob. You could check whether this blob exist or not using following code.
if (outputBlob.Exists())
{
log.Info($"520x245-{blobname}.{blobextension} is already exist");
}
else
{
log.Info($"520x245-{blobname}.{blobextension} is not exist");
//do the resize and upload the resized image to blob
}
Currently, Azure Function doesn't allow us to use CloudBlockBlob in output blob binding. A workaround is change the direction to "inout" in function.json. After that, we can use CloudBlockBlob in output blob binding.
{
"type": "blob",
"name": "outputBlob",
"path": "mycontainer/520x245-{blobname}.{blobextension}",
"connection": "connectionname",
"direction": "inout"
}
Check if your Blob exists in the container, but then you will need to add the CloudBlobContainer as input parameter as well.
CloudBlockBlob existingBlob = container.GetBlockBlobReference(blobName);
And check if it exists using
await existingBlob.ExistsAsync()
With Azure Blob storage library v12, you can use BlobBaseClient.Exists()/BlobBaseClient.ExistsAsync()
Usage is something like below,
var blobServiceClient = new BlobServiceClient(_storageConnection);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(_containerName);
BlobClient blobClient = containerClient.GetBlobClient(blobName);
bool isExists = await blobClient.ExistsAsync(cancellationToken);
BlobBaseClient.Exists(CancellationToken) Method
BlobBaseClient.ExistsAsync(CancellationToken) Method
Java version for the same ( using the new v12 SDK )
This uses the Shared Key Credential authorization, which is the account access key.
StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountName, accountKey);
String endpoint = String.format(Locale.ROOT, "https://%s.blob.core.windows.net", accountName);
BlobServiceClient storageClient = new BlobServiceClientBuilder().credential(credential)
.endpoint(endpoint).buildClient();
BlobContainerClient container = storageClient.getBlobContainerClient(containerName)
if ( container.exists() ) {
// perform operation when container exists
}

Resources