2

I've got some common functionality in a number of Azure Function Apps. Each Function App exposes the exact same query operations on a common resource that each Function App has. Same endpoints, same dependancies etc. Up until now, there's been a lot of copy paste keeping this going.

So I refactored it out into a base abstract class that lives in a common project consumed by all services. It looks like this:

    public abstract class DeadLetterApiBaseFunction : BaseFunction
    {
        private readonly IDeadLetterBlobClient _deadLetterBlobClient;
        private readonly string ROLE_READ;
        private readonly string ROLE_WRITE;
        private readonly ILogger _logger;

        public DeadLetterApiBaseFunction(
            ILogger logger,
            IMiddlewareService middleware,
            IDeadLetterBlobClient deadLetterBlobClient,
            string ROLE_READ,
            string ROLE_WRITE) : base(logger, middleware)
        {
            _logger = logger;
            _deadLetterBlobClient = deadLetterBlobClient;
            this.ROLE_READ = ROLE_READ;
            this.ROLE_WRITE = ROLE_WRITE;
        }

        [FunctionName(nameof(GetDeadLetterBlobItemsForContainer))]
        [ProducesResponseType(typeof(ResponsePayload<List<BlobItemWithContent>>), Status200OK)]
        [ProducesResponseType(Status500InternalServerError)]
        [ProducesResponseType(Status400BadRequest)]
        [QueryStringParameter("container", "Name of the container", DataType = typeof(string))]
        public async Task<ActionResult<ResponsePayload<List<BlobItemWithContent>>>> GetDeadLetterBlobItemsForContainer(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "v1/dead-letters/blobs-for-container")]
            HttpRequest request)
        {
            // .... do stuff 
        }
    }
}

The idea being that each service will subclass this like so:

    public class DeadLetterApiFunction : DeadLetterApiBaseFunction
    {
        public DeadLetterApiFunction(
            ILogger<DeadLetterApiFunction> logger,
            IMiddlewareService middleware,
            IDeadLetterBlobClient deadLetterBlobClient) : base(
                logger: logger, 
                middleware: middleware, 
                deadLetterBlobClient: deadLetterBlobClient,
                ROLE_READ: "blarg",
                ROLE_WRITE: "blarg2")
        {
        }
    }

Looks great right? However, when you run the function, the endpoints are not being exposed - the functions are not being detected.

Is there a way to get Azure Functions HttpTrigger functions exposed from a base class? Or is that just not supported? We're using .NET Core 3.1 and Azure Functions 3.

There's a related question here, but the answer took a different path:

Use Azure Functions in abstract base class?

John Reilly
  • 5,791
  • 5
  • 38
  • 63
  • In this above example are you getting one function end point coming at "v1/dead-letters/blobs-for-container" and are you expecting multiple to come up based on whats in your derived classes? – Ricky Gummadi Sep 10 '22 at 10:58
  • I have multiple separate Azure Function projects which all have a reference to a shared project which contains the DeadLetterApiBaseFunction class. Each separate project features a DeadLetterApiFunction class which inherits from the DeadLetterApiBaseFunction class. I'm not getting any function end points surfaced from those subclasses at all in the separate Azure Function projects - it's as though the DeadLetterApiBaseFunction public methods that are presumably being inherited by the DeadLetterApiFunction don't exist. – John Reilly Sep 10 '22 at 11:29

1 Answers1

2

As an HTTP trigger is a single point of entry for a specific route, I would abstract out the HTTP trigger in a separate class and then inject it in the DeadLetterApiFunction class upon creation as a Singleton via the Startup.cs file. This is possible as Azure functions support instance functions to enable DI.

public class HttpTrigger
{
    public HttpTrigger()
    {
    }

    [FunctionName(nameof(GetDeadLetterBlobItemsForContainer))]
    [ProducesResponseType(typeof(ResponsePayload<List<BlobItemWithContent>>), Status200OK)]
    [ProducesResponseType(Status500InternalServerError)]
    [ProducesResponseType(Status400BadRequest)]
    [QueryStringParameter("container", "Name of the container", DataType = typeof(string))]
    public async Task<ActionResult<ResponsePayload<List<BlobItemWithContent>>>> GetDeadLetterBlobItemsForContainer(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "v1/dead-letters/blobs-for-container")]
        HttpRequest request)
    {
        // .... do stuff 
    }
}
Mo Nazemi
  • 2,618
  • 2
  • 16
  • 23
  • I've done this as my stopgap measure already - it's a good call. But it doesn't answer my actual question; which is can I use inheritance to implement HttpTrigger functions in another projects base class and inherit that base class in an Azure Function to expose the endpoints. I can't work out if I'm doing it wrong or if it is explicitly unsupported. – John Reilly Sep 11 '22 at 15:34
  • I think HTTP trigger functions are special and cannot be viewed as a normal instance function in the base class. While in your use case you may have only one instance of the derived class but there is nothing that stops having multiple instances of the derived class. So in theory you should be able to get multiple instances of the HTTP trigger function too. However, there is a one-to-one relation between an HTTP trigger function and a route. This is probably the reason why Azure runtime expected all HTTP Trigger functions to be static until this was relaxed to enable DI in 2019 – Mo Nazemi Sep 11 '22 at 20:56
  • I suspect you're probably right - certainly seems that way! Is there any documentation about this that you're aware of? – John Reilly Sep 12 '22 at 06:00
  • 1
    I have not seen any. Maybe it is worth asking from the guys who implemented the runtime: https://github.com/Azure/azure-functions-host – Mo Nazemi Sep 12 '22 at 10:53