(This article refers to Azure Functions v2)
Out of the box, Azure Functions comes with a range of triggers, input bindings, and output bindings to work with blobs, queues, HTTP, etc.
You can also create your own input and/or output bindings.
Overview of Custom Azure Function Bindings
The general process to create a custom binding is:
- Create a class library (e.g. .NET Standard)
- Create a class that implements IAsyncCollector<T>
- Implement the AddAsync method from the interface and add code to perform some output
- Create a custom C# attribute (check out my course if you’ve never created custom attributes before ) to represent the binding attribute that will be used in functions
- Create a class that implements IExtensionConfigProvider that wires up the new binding
- In the Function App, create a startup class to register your custom extension
An Example Scenario – Integrating with Pushover
Pushover is a service (with accompanying phone app) that lets you send notifications to your phone.There is a 7 day free trial to start with and the simplest way to get started is to search for the Pushover app in the app store on your phone. Once registered you can follow the instructions to set up your application/device that push notifications can be sent to. At the end of this process, ultimately you will end up with a user key and and application API token key. Both of these are needed when calling the Pushover API.
In the following example, a custom Azure Function output binding will be created that can be used from any Azure Function to send notifications. The output could be anything however, for example you could replace the Pushover API call with a Twitter call, LinkedIn call, etc.
The following example creates an output binding but you can also create bindings that get input and passes it to a function.
Creating a POCO to Represent a Notification Message
In the new class library project, add a class:
namespace PushoverBindingExtensions
{
public class PushoverNotification
{
public string Title { get; set; }
public string Message { get; set; }
}
}
Implementing an IAsyncCollector
The next step is to create a class that implements IAsyncCollector<T> where T is the “data” that we want to pass to the output binding; in our case its the custom POCO class we just created but this could be a primitive type such as a string.
using Microsoft.Azure.WebJobs;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace PushoverBindingExtensions
{
internal class PushoverNotificationAsyncCollector : IAsyncCollector<PushoverNotification>
{
private static readonly HttpClient _httpClient = new HttpClient();
private PushoverAttribute _pushoverAttribute;
public PushoverNotificationAsyncCollector(PushoverAttribute attribute)
{
_pushoverAttribute = attribute;
}
public async Task AddAsync(PushoverNotification notification, CancellationToken cancellationToken = default(CancellationToken))
{
await SendNotification(notification);
}
public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
{
return Task.CompletedTask;
}
private async Task SendNotification(PushoverNotification notification)
{
var parameters = new Dictionary<string, string>
{
{ "token", _pushoverAttribute.AppToken },
{ "user", _pushoverAttribute.UserKey },
{ "title", notification.Title },
{ "message", notification.Message }
};
var response = await _httpClient.PostAsync("https://api.pushover.net/1/messages.json", new FormUrlEncodedContent(parameters));
response.EnsureSuccessStatusCode();
}
}
}
The key point in the preceding code is the implemented AddAsync method. This method gets called by your function when you use the binding, in this example it’s calling into the SendNotification method that talks to the Pushover API.
Notice that the constructor takes an instance of a PushoverAttribtue which we’ll define next.
Defining a Custom Binding Attribute for Azure Functions
The following code defines a .NET attribute that will be used to decorate parameters in function run methods:
using Microsoft.Azure.WebJobs.Description;
using System;
namespace PushoverBindingExtensions
{
[Binding]
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
public class PushoverAttribute : Attribute
{
public PushoverAttribute(string appToken, string userKey)
{
AppToken = appToken;
UserKey = userKey;
}
[AutoResolve]
public string AppToken { get; set; }
[AutoResolve]
public string UserKey { get; set; }
}
}
Note in the preceding code that the [Binding] attribute has been applied and the attribute has been limited to use on parameters and return values. (You can learn more about how to create custom attributes in my Pluralsight course).
This attribute definition has a couple of string properties to represent the Pushover user and app tokens. Notice these properties have been decorated with the [AutoResolve] attribute. This enables binding expressions for the properties and allows them to be read from AppSettings with %% syntax.
Creating the Azure Function Binding Custom Extension
The next step is to create a class that implements the IExtensionConfigProvider interface. This class will define the rules that are applicable to the custom binding:
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Description;
using Microsoft.Azure.WebJobs.Host.Config;
namespace PushoverBindingExtensions
{
[Extension("PushoverExtensions")]
public class PushoverExtensions : IExtensionConfigProvider
{
public void Initialize(ExtensionConfigContext context)
{
var rule = context.AddBindingRule<PushoverAttribute>();
rule.BindToCollector<PushoverNotification>(BuildCollector);
}
private IAsyncCollector<PushoverNotification> BuildCollector(PushoverAttribute attribute)
{
return new PushoverNotificationAsyncCollector(attribute);
}
}
}
The preceding class is decorated with the [Extension] attribute to mark the class as an extension and the Initialize method is where the binding rules are defined. In this method we can add binding rules and also custom convertors (for example if we wanted to be able to bind to IAsyncCollector<string> and this be automatically converted to IAsyncCollector<PushoverNotification>). In this example we’re not defining any such convertors.
Once the binding rule has been added (for the PushoverAttribute we created earlier) it can be configured as an output binding by calling the BindToCollector method. This method is used to create an instance of the PushoverNotificationAsyncCollector we created earlier, in this example this is done by calling the BuildCollector method that returns a IAsyncCollector<PushoverNotification> instance.
So hopefully your still with me; at this point we have the custom binding created in the class library project, we can now actually use it in our functions.
Using a Custom Binding in an Azure Function
In the function app project, add a reference to the class library project containing the custom binding.
We can now use the custom binding just as we would any of the pre-supplied ones as the following function code demonstrates:
[FunctionName("SendPushoverNotification")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
[Pushover("%appkey%", "%userkey%")] IAsyncCollector<PushoverNotification> notifications,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
// validation omitted for demo purposes
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
await notifications.AddAsync(new PushoverNotification { Title = data.Title, Message = data.Message });
return new OkResult();
}
The preceding function code just happens to have an HTTP trigger but the custom binding can be used in functions with other triggers as well.
Notice the [Pushover] custom binding attribute being applied. The attribute decorates the notifications parameter that is of type IAsyncCollector<PushoverNotification>. (Hopefully it’s a bit clearer now how all the parts fit together…).
Now in the function body code, a notification can be send by calling the AddAsync method and passing the PushoverNotification to be sent.
Also notice in the [Pushover] attribute the binding expressions to the app key and user key stored in app settings (or local.settings.json in the development environment) This means these sensitive keys do not need to be hard coded.
There are a few final steps to getting this all to work.
The first is to register the custom binding extension by creating the following Startup class in the function app project:
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using PushoverBindingExtensions;
namespace DontCodeTiredDemosV2
{
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.AddExtension<PushoverExtensions>();
}
}
}
And in the AssemblyInfo.cs (which you can create manually) add the following:
using Microsoft.Azure.WebJobs.Hosting;
// Register custom extension of Function App startup
[assembly: WebJobsStartup(typeof(DontCodeTiredDemosV2.Startup))]
This points to the Startup class we just created.
Testing It All Out
Now just run the function app and post the following JSON to the function address (e.g. http://localhost:7071/api/SendPushoverNotification):
{
"Title" : "Functions App",
"Message" : "I like cheese!"
}
This will result in the Azure Function executing and sending a request to the Pushover API, will will result in a notification arriving on your phone as the following screenshot shows:
I hope that’s help clarify the process a little on how to create custom binding expressions in Azure Functions. If you like me to throw the code up on GitHub let me know :)
If you want to fill in the gaps in your C# knowledge be sure to check out my C# Tips and Traps training course from Pluralsight – get started with a free trial.
SHARE: