Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom bindings in Azure Function not getting resolved

I'm trying to create my own custom binding for Azure Functions. This work is based on 2 wiki articles concerning this feature: https://github.com/Azure/azure-webjobs-sdk/wiki/Creating-custom-input-and-output-bindings and https://github.com/Azure/WebJobsExtensionSamples

For a sample project, I'm referring to the Azure Functions/WebJobs binding extension sample project. This project is based on the .NET Framework 4.6.

I want my own custom binding to work with Azure Functions v2, so I'm targetting NetStandard2 in all of my projects.

In the sample project, I see the binding extension is getting loaded when starting the local emulator.

[3-1-2019 08:48:02] Loaded binding extension 'SampleExtensions' from 'referenced by: Method='FunctionApp.WriterFunction.Run', Parameter='sampleOutput'.'

However, I never see this line appearing when running my own binding extension.

What I've done is the following. First, I've created a new attribute.

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
[Binding]
public class MySimpleBindingAttribute : Attribute
{
    /// <summary>
    /// Path to the folder where a file should be written.
    /// </summary>
    [AutoResolve]
    public string Location { get; set; }
}

And a rather simple extension class

public class MySimpleBindingExtension : IExtensionConfigProvider
{
    public void Initialize(ExtensionConfigContext context)
    {
        var rule = context.AddBindingRule<MySimpleBindingAttribute>();
        rule.BindToInput<MySimpleModel>(BuildItemFromAttribute);
    }

    private MySimpleModel BuildItemFromAttribute(MySimpleBindingAttribute arg)
    {
        string content = default(string);
        if (File.Exists(arg.Location))
        {
            content = File.ReadAllText(arg.Location);
        }

        return new MySimpleModel
        {
            FullFilePath = arg.Location,
            Content = content
        };
    }
}

And of course, the model.

public class MySimpleModel
{
    public string FullFilePath { get; set; }
    public string Content { get; set; }
}

From what I've been reading on the wiki I think this should be enough to use it in my Azure Functions project. The Function looks like this.

[FunctionName("CustomBindingFunction")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{name}")]
    HttpRequest req,
    string name,
    [MySimpleBinding(Location = "%filepath%\\{name}")]
    MySimpleModel simpleModel)
{
    return (ActionResult) new OkObjectResult(simpleModel.Content);
}

When running this in the emulator I'm seeing the following error & warning messages.

[3-1-2019 08:51:37] Error indexing method 'CustomBindingFunction.Run'

[3-1-2019 08:51:37] Microsoft.Azure.WebJobs.Host: Error indexing method 'CustomBindingFunction.Run'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'simpleModel' to type MySimpleModel. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

[3-1-2019 08:51:37] Function 'CustomBindingFunction.Run' failed indexing and will be disabled.

[3-1-2019 08:51:37] No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

Now, this message makes sense in a WebJob scenario, but for an Azure Function you con't have a place to set something up in the startup code. Invoking the Function throws the following message.

[3-1-2019 08:53:13] An unhandled host error has occurred.

[3-1-2019 08:53:13] Microsoft.Azure.WebJobs.Host: 'CustomBindingFunction' can't be invoked from Azure WebJobs SDK. Is it missing Azure WebJobs SDK attributes?.

I've read something about adding an extensions.json file and use the AzureWebJobs_ExtensionsPath (for v1), but those don't appear do do anything (or I've done something wrong).

Any ideas on how to proceed?

For completeness sake, here are my current references to NuGet packages.

<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.3" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.24" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
like image 225
Jan_V Avatar asked Jan 03 '19 09:01

Jan_V


People also ask

Can an azure function have multiple bindings?

Bindings are optional and a function might have one or multiple input and/or output bindings. Triggers and bindings let you avoid hardcoding access to other services. Your function receives data (for example, the content of a queue message) in function parameters.

How many input binding is an azure function allowed?

A trigger defines how the function is called upon; however, every function has one trigger and optional bindings.

How do you trigger http events in serverless?

HTTP Trigger Azure Functions has an API endpoint created for each Function App. This service allows you to define public HTTP endpoints for your serverless functions. To create HTTP endpoints as Event sources for your Azure Functions, use the Serverless Framework's easy HTTP Events syntax.


1 Answers

With some help and guidance of other examples I was able to figure out what needs to be added at the moment to get your custom bindings working.

My guess is this will change over time in upcoming releases of the runtime, but who knows...

First, add a small extension method in which you add your extension to the IWebJobsBuilder.

public static class MySimpleBindingExtension
{
    public static IWebJobsBuilder AddMySimpleBinding(this IWebJobsBuilder builder)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        builder.AddExtension<MySimpleBinding>();
        return builder;
    }
}

Next is the IWebJobsStartup implementation. This will get picked up on startup by the runtime via reflection.

public class MySimpleBindingStartup : IWebJobsStartup
{
    public void Configure(IWebJobsBuilder builder)
    {
        builder.AddMySimpleBinding();
    }
}

One thing you must not forget is to add the the following to this class:

[assembly: WebJobsStartup(typeof(MySimpleBindingStartup))]

You'll notice this soon enough, because if you forget, the binding won't get picked up.

Hope this helps for everyone hitting the same issue I had. If you want to see the complete code, I've got a sample project in a GitHub repository available: https://github.com/Jandev/CustomBindings

like image 129
Jan_V Avatar answered Oct 02 '22 07:10

Jan_V