Azure function blob BlobPath to lowercase - c#

I have an Azure function like this
public async Task<IActionResult> GetProducts
(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "products/{prod}/all")] HttpRequest req,
[Blob("%prodPath%", FileAccess.Read, Connection = "AzureWebJobsStorage")] Stream myBlobRead,
ILogger log, string prod)
{
var result = await _function.get(myBlobRead).ConfigureAwait(false);
return result;
}
)
The blob reads the file path from config json file.
Inside the config json file, there is a variable and a value like this:
"prodPath": "products/{prod}-all.csv"
if the user do a get request like this => www.xxx.com/api/products/table/all
everything is fine because the blob file name called table-all.csv
but if the user tries to do a get request like this => www.xxx.com/api/products/Table/all, its will fail becuase the name does'nt match.
Can you please help me with that?
I tried to change the variable in the config file to
[tolower("prodPath")]: "products/{prod}-all.csv"
but same problem.
How to change the prod to lowercase ?
Thank you

I may suggest using the runtime binding technique (article for c# and c# script). The idea is that instead of using the blog attribute you use the IBinder parameter and then invoke this binding in your function body. Obviously, you can now evaluate any parameter you want. Note, that you should remove the expression from the json file if you use this technique.
Here is a sample from MSDN combined with bit of your code:
public static class IBinderExample
{
[FunctionName("CreateBlobUsingBinder")]
public static void Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "products/{prod}/all")] HttpRequest req,
IBinder binder)
{
var prodPath = $"products/{prod}-all.csv".ToLower();
// provide connection string and optionally change TextWriter to whatever you need
using (var writer = binder.Bind<TextWriter>(new BlobAttribute(
prodPath, FileAccess.Write)))
{
writer.Write("Hello World!");
};
}
}

Related

Consuming SignalR services in c# serverless inside the code

Ivé this code...
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "v1/Sala")]
HttpRequest req, [SignalR(HubName = "{query.HubName}")]
IAsyncCollector<SignalRMessage> signalRMessages) {
await signalRMessages.AddAsync(
new SignalRMessage {
Target = "newMessage",
Arguments = new[] { "Hello" }
});
}
And this code works good and I am happy. But I've a need, in partiular on my EventGridTrigger...
As you could noticed on the code above, the hubname is dinamic and an EventGridTrigger is a sort of special kind of endpoint (Your client app will not call and consume it...SignalR will instead).
But I am capable to identify the hubname on my EventGridTrigger...I can do this:
SignalRDataEvent data =
JsonConvert.DeserializeObject<SignalRDataEvent(eventGridEvent.Data.ToString());
string hubname = data.hubname;
But now...I need to send a signalR message using my variable hubname. Since I can't put [SignalR(HubName = "{query.HubName}")] IAsyncCollector signalRMessages) on my EventGridTrigger, I need to create the object SignalR, probably pass credentials, hubname, etc and then send a message. I can't find any sample for this - At least no samples that can work in serverless c# azure functions. Can someone help me wikth this ?
Try the following:
[SignalR(HubName = "{data.hubName}")]

How I can access all fields of a http-request in an azure function (parse JSON in C#)?

The microsoft azure is for me complete new programming topic.
Programming base language is C#.
I have to use Azure Funtion Http trigger from logic App (when new e-mail arrive).
I give all possible data from the incoming e-mail in the log app to
to the azure function call.
Then I had first the problem nothing (no data) was requestable from the HttpRequest req.
I found out that C# .NetCore3.1 has problem with JSON-Object and get null-object back.
Then I build the project with .NetCore2.1 and it worked generally.
I get correct informations like req.Host and i see a vaild JSON-object in data after such a call
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
But I don't know how I an parse the data like (from, to, subject, content, attachment,..) from the JSON-object.
I have tried many samples i found , but i fond nothing what worked, e.g. get null, exception,..
Can please someone here post a small code snippet that works for azure function compiled with VS2017/2019 .NetCore2.1..
And other question is there eventually a way get the MimeMessage-obj direct from the http-request?
then i can get the informations direct from that object, but helping me with one of this both solution
where fine.
Otherwise I spilit the the JSoN Structure on myself to get the the informations on a very awkward way.
So I hope someone can help me.
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation($" host : {req.Host}");
string name = req.Query["name"];
// read the contents of the posted data into a string
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
// use Json.NET to deserialize the posted JSON into a C# dynamic object
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
For Example i had such a Json-content (is in dtaa after thes 2 lines of code):
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
Json-Content for a short email to parse (other request could differn in more ore lesser paramters(fields))
{ {
"Conversion Id": "AAQkADRmZDAwMTMwLTI3MTMtNGI0Ny1iMzFiLTQzYWJiZDY0YWI1ZQAQAE2OZM06t0uQqpd9MuONKNQ=",
"From": "test1#testxyz.com",
"Has Attachment": false,
"Internet Message Id": "<AM0PR07MB44178000A0B37882D6BC01D99ACC0#AM0PR07MB4417.eurprd07.prod.outlook.com>",
"Is Html": false,
"Is rRad": false,
"Message Id": "AAMkADRmZDAwMTMwLTI3MTMtNGI0Ny1iMzFiLTQzYWJiZDY0YWI1ZQBGAAAAAAAvlJZPJxnGS4f6YWxh7zGsBwBnLziSnuG8R6h5C2SVmlYlAAHViSWpAACAWl3JfUo4SI7D5g-MgfEiAAJiPJQeAAA=",
"Received Time": "2020-12-09T14:05:06+00:00",
"Subject": "test1",
"To": "test2#testxyz.com",
"emailBody": "1234\r\n",
"importance": "normal"
}
}
For this requirement, you need to create an inner class in your function code to parse the request body to an object. Please refer to my code below:
using System;
using System.IO;
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;
namespace FunctionApp2
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
//dynamic data = JsonConvert.DeserializeObject(requestBody);
EmailItem data = JsonConvert.DeserializeObject<EmailItem>(requestBody);
log.LogInformation(data.From);
log.LogInformation(data.Has_Attachment.ToString());
return new OkObjectResult("completed");
}
}
public class EmailItem
{
public string From { get; set; }
public Boolean Has_Attachment { get; set; }
//Add other fields which you want, please note add all of the fields which your request may contains in this inner class.
}
}
As you mentioned other request could differn in more ore lesser paramters(fields), so please add all of the fields which your request may contains in the EmailItem class.
By the way, the code I provided above was test with .net core 2.1 as you required. But I think the code can also work in .net core 3.1

"Could not load file or assembly 'PresentationCore'" error in azure function using Gembox.Document

I currently use Gembox.Document to read content from PDF documents. I have a class library that houses it all and a self hosted .NET Core 3.1 service that references it. I query the service with the PDF data and it responds with the content. I now want to move this functionality to an azure function (v3) instead, but I am running into the following error:
Could not load file or assembly 'PresentationCore, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot
find the file specified.
To simplify it I have moved only the essential parts into the azure function which you can see below:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
ComponentInfo.FreeLimitReached += (sender, args) => args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial;
try
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
ParseRequest data = JsonConvert.DeserializeObject<ParseRequest>(requestBody);
StringBuilder sb = new StringBuilder();
using (var ms = new MemoryStream(data.Data))
{
// Load document from file's path.
var document = DocumentModel.Load(ms, LoadOptions.PdfDefault);
foreach (var childElement in document.GetChildElements(true, ElementType.Paragraph))
{
sb.AppendLine(childElement.Content.ToString());
}
}
return new OkObjectResult(sb.ToString());
}
catch (Exception e)
{
return new OkObjectResult("Unable to read document");
}
}
Is this a restriction of azure functions? I have read several conflicting things which suggest it can and can't be done as it's using a WPF dll. For the record, the GemBox website provides an example for creating a PDF document in an azure function: https://www.gemboxsoftware.com/document/examples/create-word-pdf-on-azure-functions-app-service/5901. So I don't see why I wouldn't be able to read one too!
Thanks!
EDIT 1:
As per mu88's comment, I have changed the .csproj file to the below and it hasn't helped.
I reached out to GemBox support and they responded with the following:
Unfortunately, GemBox.Document uses WPF for reading PDF files. So,
even though you can write PDF files on Azure Functions, I’m afraid you
cannot read them.
But also, I should point out that PDF reader in GemBox.Document never
left the BETA stage, it has limited usage. For more information,
please check the Support level for reading PDF format (beta) section.
Instead, I would suggest you try out GemBox.Pdf, see its Reading
example. With GemBox.Pdf you can read and write PDF files on Azure
Functions.
Last, in the long term, we plan to replace the current (internal)
implementations of both PDF reader (BETA) and PDF writer in
GemBox.Document with a newer implementation that’s contained in
GemBox.Pdf without changing the public API of GemBox.Document. But
that will not be done in the current year and for later I cannot say
at this moment.
Alas, it is not possible with GemBox.Document.. yet.
GemBox.Document has this issue (it might be dependent on .net Framework component), but GemBox.Pdf works fine as below.
I tested it using the GemBox.Pdf nuget with the below Function and it works fine for both creating and loading pdf in deployed Function app.
Create PDF:
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
using (var document = new PdfDocument())
{
// Add a page.
var page = document.Pages.Add();
// Write a text.
using (var formattedText = new PdfFormattedText())
{
formattedText.Append("Hello World!");
page.Content.DrawText(formattedText, new PdfPoint(100, 700));
}
var fileName = "Output.pdf";
var options = SaveOptions.Pdf;
using (var stream = new MemoryStream())
{
document.Save(stream, options);
return new FileContentResult(stream.ToArray(), "application/pdf") { FileDownloadName = fileName };
}
}
}
LoadPDF:
[FunctionName("Function3")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
try
{
StringBuilder sb = new StringBuilder();
using (var document = PdfDocument.Load(req.Body))
{
foreach (var page in document.Pages)
{
sb.AppendLine(page.Content.ToString());
}
}
return new OkObjectResult(sb.ToString());
}
catch (Exception e)
{
return new ExceptionResult(e, true);
}
}

Azure Function output binding with Azure.Cosmos.Table

I have a simple azure function as below
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[Table("MyTable")] CloudTable cloudTable,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var entity = new CustomerEntity("Jon", "Snow")
{
Email = "babc#email.com",
PhoneNumber = "04026753432"
};
// Create the InsertOrReplace table operation
TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);
// Execute the operation.
TableResult result = await cloudTable.ExecuteAsync(insertOrMergeOperation);
CustomerEntity insertedCustomer = result.Result as CustomerEntity;
// Get the request units consumed by the current operation. RequestCharge of a TableResult is only applied to Azure Cosmos DB
if (result.RequestCharge.HasValue)
{
Console.WriteLine("Request Charge of InsertOrMerge Operation: " + result.RequestCharge);
}
return new OkObjectResult($"Hello");
}
}
I am using Microsoft.Azure.Cosmos.Table instead of Microsoft.WindowsAzure.Storage.Table. However the "Table" attribute is not being recognize. Is there any extension nuget that I need to include here ?
-Alan-
It sounds like you may be trying to mix and match SDKs. The binding that you are using in the function is not using the Cosmos version of the table SDK, so you either need to use the storage one outright, or not leverage a binding and just use the Cosmos Table SDK directly to create your own instance of a cloudTable
Add Microsoft.Azure.WebJobs.Extensions.Storage package and your done

azure function c# http trigger blob output

Can someone describe me how I can configure a C# azure function which uses an HTTP input trigger and a blob storage output trigger?
Maybe also with an example code snippet and an example function.json. I don't get it to work locally with the azure functions core tools.
This is a combined HTTP triggered function with a output blob binding:
[FunctionName("HttpTriggeredFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest httpRequest,
[Blob("blobcontainer", Connection = "StorageConnectionString")] CloudBlobContainer outputContainer,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
await outputContainer.CreateIfNotExistsAsync();
var requestBody = await new StreamReader(httpRequest.Body).ReadToEndAsync();
var blobName = Guid.NewGuid().ToString();
var cloudBlockBlob = outputContainer.GetBlockBlobReference(blobName);
await cloudBlockBlob.UploadTextAsync(requestBody);
return new OkObjectResult(blobName);
}
It uses the CloudBlobContainer output type to get a reference to the blob container which then enables you to use methods such as .GetBlockBlobReference("blobPath") to get a reference to a blob.
Once you have a reference to a blob, you can use different methods to upload:
cloudBlockBlob.UploadFromByteArrayAsync()
cloudBlockBlob.UploadFromFileAsync()
cloudBlockBlob.UploadTextAsync()
cloudBlockBlob.UploadFromStreamAsync()
To get it running locally, you need set some things up. Notice in my example the attribute [Blob("blobcontainer", Connection = "StorageConnectionString")]
"blobcontainer" this can be whatever you want and will be the name of the container that will be created in your storage account by this line outputContainer.CreateIfNotExistsAsync(); (if it doesn't exist already).
Connection = "StorageConnectionString" this can be a setting in your local.settings.json for the connection string of your storage account. When developing locally I would recommend setting this to "UseDevelopmentStorage=true" so that you can take advantage of the storage emulator. Then when you are ready to deploy onto Azure, you would create a setting in the function app containing the real connection string.
local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"StorageConnectionString": "UseDevelopmentStorage=true"
}
}
to make an http function that saves to Blob Storage use this code:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log,TextWriter outputBlob)
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
outputBlob.WriteLine(requestBody);
string result = "{ 'result': 'ok' }";
dynamic data = JsonConvert.DeserializeObject(result);
return new OkObjectResult(data);
}
You need to set the output binding:
You can then run a test posting content on the test window
Everything you need is there in the Official docs page,
(i) Http and WebHooks
(ii)Output binding blob storage
Http Trigger Sample code
[FunctionName("HttpTriggerCSharp")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequest req, ILogger log)
Blob Storage Output binding
[FunctionName("ResizeImage")]
public static void Run(
[BlobTrigger("sample-images/{name}")] Stream image,
[Blob("sample-images-sm/{name}", FileAccess.Write)] Stream imageSmall,
[Blob("sample-images-md/{name}", FileAccess.Write)] Stream imageMedium)

Categories

Resources