Azure / azure-functions-dotnet-worker

Azure Functions out-of-process .NET language worker
MIT License
419 stars 184 forks source link

Isolated process - Definite documentation on how to write custom binding and triggers, and troubleshoot section #2092

Open Kryptos-FR opened 10 months ago

Kryptos-FR commented 10 months ago

Hello, I have a hard time making anything work in the isolated worker model. All the available doc is about the in-process model, which is ironic since Microsoft is pushing us towards the other one.

Where can I find a definitive documentation on how to write custom bindings and triggers for the isolated worker?

For example, I just want to be able to inject a class into my functions. But the only place it works is on the class constructor, while I want one per-function call.

Pseudo code:

// Types to inject
public interface IMyType {}
public class MyType : IMyType {}

// Register them
[assembly: WorkerExtensionStartup(typeof(MyExtensionStartup))]
public sealed class MyExtensionStartup : WorkerExtensionStartup
{
    public override void Configure(IFunctionsWorkerApplicationBuilder applicationBuilder)
    {
        applicationBuilder.Services.AddSingleton(typeof(IMyType), typeof(MyType));
    }
}

// Function
public class MyFunction
{
    private readonly IMyType _my;
    public MyFunction(IMyType> my)
    {
        _my = my; // OK
    }

    [Function("Try_Get")]
    public async Task<HttpResponseData> Get(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequestData request,
        CancellationToken token,
        IMyType? my = null
    )
    {
        // my is always null
        return request.CreateResponse(HttpStatusCode.OK);
    }
}

I also tried using an attribute but I don't know how to make it work and there is no documentation for that:

[AttributeUsage(AttributeTargets.Parameter)]
public class MyBindingAttribute : BindingAttribute
{

}

// In MyFunction class

    [Function("Try_Get")]
    public async Task<HttpResponseData> Get(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequestData request,
        CancellationToken token,
        [MyBinding] IMyType? my = null
    )

Using an "input" binding instead brings an error:

[AttributeUsage(AttributeTargets.Parameter)]
public class MyBindingInputAttribute : InputBindingAttribute
{

}

// In MyFunction class

    [Function("Try_Get")]
    public async Task<HttpResponseData> Get(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequestData request,
        CancellationToken token,
        [MyBindingInput] IMyType? my = null
    )

The 'Try_Get' function is in error: The binding type(s) 'myBinding' are not registered. Please ensure the type is correct and the binding extension is installed.

Would be nice to have a documentation page to troubleshoot those errors. It is pointless to report them if there is no way to find a solution.

gallivantor commented 10 months ago

Agreed. There seems to be no documentation at all for how to build a custom binding with the isolated worker model. The best I could find was that building on top of your example you need to build a class that implements IInputConverter and then attach it to the InputAttribute using this: [InputConverter(typeof(MyInputConverter))]

But that is also not enough to get the binding registered, I get the same error message as you:

The 'Try_Get' function is in error: The binding type(s) 'myBinding' are not registered. Please ensure the type is correct and the binding extension is installed.

DadvDadv commented 10 months ago

Do you know if there is any IExtensionConfigProvider equivalent ?

fabiocav commented 10 months ago

@Kryptos-FR can you share more details about the scenario you expect to handle with your custom binding? I'm assuming the one you've shared above is a simple example, so it would be good to understand your concrete requirement.

The reason why I'm asking is because custom bindings, and particularly triggers, have very limited support in Functions (this is not specific to isolated, as it applies to the in-proc model as well), and there are new extensibility options exposed by isolated that we might be able to recommend if we better understand your scenario.

Kryptos-FR commented 10 months ago

@fabiocav Let me setup a small repo to illustrate our use. I'll edit this comment once it's ready.

edit: available at https://github.com/Kryptos-FR/azure-functions-dotnet-worker-2092

mikoskinen commented 7 months ago

Ended up in this issue after trying to find documentation on how to convert our existing WebJobs based function trigger to work with this new model.

Let's say that I would like to develop a custom trigger that works with something like Apache Pulsar or NATS or something which isn't available out of the box. Where do I start? For example I have a custom NATS trigger working but it uses the webjob based sdk and now with the new model, there's this:

Error AZFW0001 : The attribute 'NatsTriggerAttribute' is a WebJobs attribute and not supported in the .NET Worker (Isolated Process). (https://aka.ms/azfw-rules?ruleid=AZFW0001)

Is there a migration guide about this subject?

Thanks in advance.

Kryptos-FR commented 7 months ago

@fabiocav do you have any guidance on the subject, especially regarding the sample I provided? What are the others ways you mentioned?

mattramsay-ice commented 7 months ago

Currently encountering the same black hole regarding documentation on custom trigger bindings in the world of isolated worker functions.

It's currently blocking us from migrating to the isolated worker model.

Other than a couple of articles and trying to reverse engineer the existing worker extensions for OOTB triggers/input/output bindings, there really isn't anything out there.

If custom triggers is something the Azure functions team don't intend to support going forward (as alluded to in an earlier comment), then that should be stated explicitly.

DadvDadv commented 7 months ago

For input binding, i found that we could use InputConverterAttribute

using Microsoft.Azure.Functions.Worker.Converters;

 public class FromIfMatchHeaderAttribute : InputConverterAttribute
 {
     /// <summary>
     /// Creates an instance of the <see cref="FromEtagAttribute"/>.
     /// </summary>
     public FromIfMatchHeaderAttribute()
         : base(typeof(IfMatchConverter))
     {

     }
 }

    internal class IfMatchConverter : IInputConverter
    {
        public ValueTask<ConversionResult> ConvertAsync(ConverterContext context)
        {
            var httpRequestData = context.FunctionContext.GetHttpRequestDataAsync().GetAwaiter().GetResult();

            if (httpRequestData != null)
            {
                var etag = httpRequestData.IfMatchHeader(); //Custom code for catch IFMatch header

                return new ValueTask<ConversionResult>(ConversionResult.Success(etag));
            }

            return new ValueTask<ConversionResult>(ConversionResult.Success(null));
        }
    }

Usage :

       public async Task<HttpResponseData> Update(
           [HttpTrigger(AuthorizationLevel.Function, "put", Route = "customers/{id}")] HttpRequestData req,
           [FromBody] UpdateDictionaryCommand model, //From Body it's the only one attribute i've found in the new isolated process
           string? id, //From Route by default
           [FromIfMatchHeader] string? etag //From header
           )
{
...
}

Unfortunatly, you couldn't replace the "req" variable (i didn't use it but it's requiered for HttpTrigger.

It's not perfect, but it could do the job some time

I try to use TriggerBindingAttribute to create my own, but without success (I think we need to create some kind of middleware to do the data conversion).

molinomatteo commented 6 months ago

Hello, same problem here. We have many custom bindings that we need to migrate to Workers.

For example we implemented Coap, Mqtt, Mongo, OpcUa, Redis, IotHub (fe: registry input, ack trigger and cloud to device messages).

By reading some articles and looking at your workers implementation (EventHub, ServiceBus, Blob etc) I understood that in every new project I have to:

So my questions are:

ivelten commented 5 months ago

I am having the same problem here.

Our company uses in-proc input and output bindings that we want to migrate to the isolated worker, but the documentation we found so far is not enough, especially to help understand how to create and use custom output bindings.

jaliyaudagedara commented 4 months ago

Stumbled upon this.

Any update guys?

perum commented 3 months ago

This is a serious problem... We use custom INPUT bindings to parse auth tokens/Claims. To convert to net8 / isolated, these need to work, and there is NO documentation, or even mention of a breaking change.

Joseph-Steven-S commented 2 months ago

Hello,

Is there any update on this?

kkdubey commented 1 month ago

Hello,

Any update on this issue? Is there any document to implement Custom attribute trigger?