azure function: set blob filename from http request - azure

I am trying to set an azure function to upload a blob from an HTTP request to a blob.
I was able to use the following for uploading a file with a static filename:
public static class Uploader
{
[FunctionName("Uploader")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequest req,
[Blob("hello/uploaded.jpg", FileAccess.Write)]Stream writer,
TraceWriter log
)
{
log.Info("trigger for image upload started...");
if (!req.ContentType.Contains("multipart/form-data") || (req.Form.Files?.Count ?? 0) == 0)
{
log.Warning("no images found on upload attempt");
return new BadRequestResult();
}
foreach (var file in req.Form.Files)
file.CopyTo(writer.);
return new OkObjectResult("Done!");
}
}
Is there any way I could alter Blob("hello/uploaded.jpg") into something like Blob("hello/{fileName}" to get the name dynamically the HTTP request. I don't mind if it's anywhere from the head or body. I am trying to not use the whole GetBlockBlobReference process just for the dynamic file name alone.
Update
I am not sure if I am missing something or looking at this problem the wrong way. For a serverless set up with a blob storage isn't an HTTP upload supposed to be an obvious and common scenario? How come there aren't any examples for this?

The option suggested by #RomanKiss should work. Alternatively, if you want you can put filename into the function URL template:
[FunctionName("Uploader")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "uploader/{filename}")]
HttpRequest req,
string filename,
[Blob("hello/{filename}", FileAccess.Write)] Stream writer,
TraceWriter log)
{
//...
}

the following is an example with both an HttpRequestMessage and a POCO binding:
[FunctionName("Uploader")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] Info input, HttpRequest req,
[Blob("hello/{FileName}", FileAccess.Write)]Stream writer,
TraceWriter log
)
{
//...
}
public class Info
{
public string FileName { get; set; }
//...
}
Update:
The following snippet shows an example for using an expanding HttpTrigger binding data support with a well-known properties such as headers and query, more details here:
[FunctionName("Uploader")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)HtpRequest req,
[Blob("hello/{query.filename}", FileAccess.Write)] Stream writer,
// [Blob("hello/{headers.filename}", FileAccess.Write)] Stream writer,
TraceWriter log)
{
//...
}
sample of url:
http://localhost:7071/api/Uploader?filename=uploaded.jpg

Related

Programmatically get function app url from event grid triggered function

In an HTTP triggered azure function we can get the url as follows
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, Microsoft.Azure.WebJobs.ExecutionContext executionContext, ILogger log)
{
string url = req.Scheme + "://" + req.Host + $"/api/AnotherFunctionOnTheApp";
}
But how do we do it in an event grid triggered function where we do not have the HTTPRequest object?
public static void Run([EventGridTrigger]EventGridEvent eventGridEvent, ILogger log)
{
}
The objective is to call an HTTP triggered function on the same functionapp from the event grid triggered function.
A simple example to get the url of the httptrigger:
string url = "https://" + Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME") + "/api/AnotherFunctionOnTheApp";
(Azure will offer a secure domain by default. So we can use 'https'.)

FromQuery support in Azure Functions v3

I am trying to use [FromQuery] with Azure Function v3 but I am getting the following error:
Cannot bind parameter 'search' to type String.
For the following method:
[FunctionName("GetObjects")]
public ActionResult<IActionResult> QueryObjects(
[HttpTrigger(AuthorizationLevel.Function, "GET", Route = "objects")]
HttpRequest req,
ILogger log,
[FromQuery] string search = null)
{
//do some stuff
}
Is [FromQuery] not supported?
Should I use req.Query["search"] to get the query parameter?
From functions.desp.json
Related to binding
"Microsoft.Extensions.Configuration.Binder/3.1.1": {
"dependencies": {
"Microsoft.Extensions.Configuration": "3.1.2"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.dll": {
"assemblyVersion": "3.1.1.0",
"fileVersion": "3.100.119.61404"
}
}
},
This is what you face now:
Method signatures developed by the azure function C # class library can include these:
ILogger or TraceWriter for logging (v1 version only)
A CancellationToken parameter for graceful shutdown
Mark input and output bindings by using attribute decoration
Binding expressions parameters to get trigger metadata
From this doc, it seems that it is not supported. You can create your custom binding like this, and dont forget to register it in the startup.
If you want to bind it directly, it's not possible. So you could try to change your route like Function1/name={name}&research={research} then bind it to string parameter.
Below is my test code:
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route="Function1/name={name}&research={research}")] HttpRequest req,
String name,
String research,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
log.LogInformation(research);
string responseMessage = $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}

ExecutionContext is null to non-function methods via IoC, alternative to ExecutionContext.FunctionAppDirectory

ExecutionContext is available to functon parameters.
However, it is not available to other methods via dependency inject, including Functions' constructor, like below:
public class FunctionClass
{
IOtherClass _otherclass;
public FunctionClass(ExecutionContext context, //excetpion
IOtherClass otherclass) //excetpion
{
_otherclass = IOtherClass otherclass
}
[FunctionName("Car")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
HttpRequest req, ExecutionContext context)
{
}
}
public class OtherClass:IOtherClass
{
public OtherClass(ExecutionContext context) //excetpion
{}
}
I need access to ExecutionContext.FunctionAppDirectory, but don't want to pass ExecutionContext around, because want to use IoC instead.
Is there an alternative way to get the value of ExecutionContext.FunctionAppDirectory?
VS 2017
Azure Functons 2.x
We can use ExecutionContextOptions to get application folder:
public class FunctionClass
private ExecutionContextOptions context;
public FunctionClass(IOptions<ExecutionContextOptions> executionContext) {
this.context = executionContext.Value;
var path = Path.GetFullPath(Path.Combine(context.AppDirectory, "extra.json"));
}
}
Note:
The above works using VS 2019 and Azure Functions 3.x
See:
Github issue
Related post
Based on current documentation, ExecutionContext is only available in the scope of a request when the function method is being invoked.
[FunctionName("Car")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
HttpRequest req,
ExecutionContext context //<--
) {
var path = context.FunctionAppDirectory;
//...
}
It wont be available as yet in the constructor for injection when the function class is initialized.

Can an azure job with a HTTP Trigger return a http response body?

I have an azure function like this:
[FunctionName("DoStuff")]
[return: Queue("stuff-queue")]
public static async Task<StuffContext> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
var context = await req.Content.ReadAsAsync<StuffContext>();
context.TransactionId = Guid.NewGuid();
return context;
}
It listens on a https url, deserializes the request body, and send body to the queue. Can I also have it return something (the transaction id in this case) as part of the http response.
Can I also have it return something (the transaction id in this case) as part of the http response.
Using [return: Queue("stuff-queue")] will return the information to the queue. But it could not return a response and add the information to the queue at the same time.
If you want to do, you could refer to my code.It works fine on my side.
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")]HttpRequestMessage req,
TraceWriter log,
[Queue("stuff-queue")] IAsyncCollector<string> outputQueue)
{
log.Info("C# HTTP trigger function processed a request.");
string yourinfo = "yourinfo";
await outputQueue.AddAsync(yourinfo);
return req.CreateResponse(HttpStatusCode.OK, yourinfo);
}
For more details, you could refer to this article.

Is Attribute Routing possible in Azure Functions

I am trying to enforce a route parameter to be guid but getting below error
"Exception while executing function: GetUser -> One or more errors
occurred. -> Exception binding parameter 'req' -> Invalid cast from
'System.String' to 'System.Guid'."
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Admin, "get", Route = "GetUser/{userId:guid}")] HttpRequestMessage req,
Guid userId, ILogger log)
{
}
The request i am making is http://localhost:7071/api/GetUser/246fb962-604d-4699-9443-fa3fa840e9eb/
Am i missing some thing? Cannot we enforce route parameter to be guid ?
Invalid cast from 'System.String' to 'System.Guid'
I can reproduce same issue when use Route constraint {userId:guid} in Azure httptrigger function on my side, you can try to open an issue to give a feedback.
Besides, if possible, you can try to call Guid.TryParse method to convert the string back to Guid value in function code, the following code is for your reference.
public static string Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "GetUser/{userId:guid}")]HttpRequestMessage req, string userId, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
Guid newGuid;
var resmes = "";
if (Guid.TryParse(userId, out newGuid))
{
resmes = "userid: " + newGuid;
}
else {
resmes = "error";
}
return resmes;
}

Resources