24

I need to bind to an output blob, but the blob path needs to be computed dynamically in my function. How do I do it?

Janusz Nowak
  • 2,595
  • 1
  • 17
  • 36
mathewc
  • 13,312
  • 2
  • 45
  • 53

2 Answers2

32

Binder is an advanced binding technique that allows you to perform bindings imperatively in your code as opposed to declaratively via the function.json metadata file. You might need to do this in cases where the computation of binding path or other inputs needs to happen at runtime in your function. Note that when using an Binder parameter, you should not include a corresponding entry in function.json for that parameter.

In the below example, we're dynamically binding to a blob output. As you can see, because you're declaring the binding in code, your path info can be computed in any way you wish. Note that you can bind to any of the other raw binding attributes as well (e.g. QueueAttribute/EventHubAttribute/ServiceBusAttribute/etc.) You can also do so iteratively to bind multiple times.

Note that the type parameter passed to BindAsync (in this case TextWriter) must be a type that the target binding supports.

using System;
using System.Net;
using Microsoft.Azure.WebJobs;

public static async Task<HttpResponseMessage> Run(
        HttpRequestMessage req, Binder binder, TraceWriter log)
{
    log.Verbose($"C# HTTP function processed RequestUri={req.RequestUri}");

    // determine the path at runtime in any way you choose
    string path = "samples-output/path";

    using (var writer = await binder.BindAsync<TextWriter>(new BlobAttribute(path)))
    {
        writer.Write("Hello World!!");
    }

    return new HttpResponseMessage(HttpStatusCode.OK); 
}

And here is the corresponding metadata:

{
  "bindings": [
    {
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "name": "res",
      "type": "http",
      "direction": "out"
    }
  ]
}

There are bind overloads that take an array of attributes. In cases where you need to control the target storage account, you pass in a collection of attributes, starting with the binding type attribute (e.g. BlobAttribute) and inlcuding a StorageAccountAttribute instance pointing to the account to use. For example:

var attributes = new Attribute[]
{
    new BlobAttribute(path),
    new StorageAccountAttribute("MyStorageAccount")
};
using (var writer = await binder.BindAsync<TextWriter>(attributes))
{
    writer.Write("Hello World!");
}
mathewc
  • 13,312
  • 2
  • 45
  • 53
  • In case the blob path should contain information that is not coming from a bound incoming object AND calculated at runtime, is it possible to use that with the path? – Sean Feldman Oct 04 '16 at 17:20
  • 1
    Perhaps my ignorance, but if no storage account is specified anywhere in the bindings, what storage account is going to be used for the output? – Sean Feldman Oct 04 '16 at 18:49
  • 2
    At this point the path is just a string. You can calculate it however you wish. By default, the default storage account indicated by the AzureWebJobsSdkStorage app setting is used. You can override the storage account used by changing your parameter type to Binder (as opposed to IBinder). It has an overload that takes an array of attributes. You can then pass a StorageAccountAttribute in along with the actual binding attribute. The binding attribute needs to come first. I'll add these details above. – mathewc Oct 04 '16 at 20:44
  • It looks like storage account variable in the settings is called AzureWebJobsStorage :) Thank you @mathewc. Now it's clear. – Sean Feldman Oct 04 '16 at 22:18
  • Fantastic. So to use a custom connection string, one must provide app setting key to the StorageAccountAttribute. Great. Thank you. – Sean Feldman Oct 04 '16 at 22:34
  • @mathewc, can you provide a link/info on how to use Binder in Node.js function? – Robert Vuković Mar 05 '17 at 18:52
  • Binder APIs are not yet available for Node.js Functions – mathewc Mar 06 '17 at 16:25
  • @mathewc with textwriter with Binder, the content-type is always `application/octet-stream`, anyway to change content type? – vicancy Jul 26 '17 at 03:35
  • Can we do this in C# Azure function? Not C# script. The sample seems C# script since it don't have class to hold Run function. Mainly we have enterprise policy to renew storage account keys and when we do so the Azure function cannot dequeue. So looking for programmatic setting of storage account for Functions to connect to Storage queue. – Joy George Kunjikkuru Dec 06 '17 at 21:36
  • What does the calling app code look like to set the Binder? – Larry Aultman Oct 01 '18 at 00:14
  • do we still do this today? – Alex Gordon Mar 29 '19 at 22:46
  • can we dynamically bind to a blob like `container/{rand-guid}` and output to this multpile times within the function? – Alex Gordon May 23 '19 at 00:48
  • having very tough time finding an example where we do imperative input bindings – Alex Gordon May 29 '19 at 02:06
  • How to write binary data to a blob using the BInder? Using TextWriter doesn't write binary data (like a PDF file uploaded to my httptrigger). – HisDivineShadow Mar 16 '21 at 17:52
8

Have consolidated all of the information from this an other posts along with comments and created a blog post that demonstrates how to use Binder with a real world scenario. Thanks to @mathewc this became possible.

Sean Feldman
  • 23,443
  • 7
  • 55
  • 80