Skip to content

Function Filters

Mathew Charles edited this page Aug 15, 2017 · 14 revisions

Overview

Function Filters are a way for you to customize the WebJobs execution pipeline with your own logic. Filters are very similar in spirit to ASP.NET Filters. They can be implemented as declarative attributes that can be applied to your job functions/classes.

Filters allow you to encapsulate common logic to be shared across many different functions. They also allow you to centralize logic for cross cutting concerns (e.g. validation, logging, error handling, etc.).

Here's an example that shows both a method level Invocation Filter as well as an Exception Filter specified at the class level:

[ErrorHandler]
public static class Functions
{
    public static void BlobTrigger(
        [BlobTrigger("test")] string blob)
    {
        Console.WriteLine("Processed blob: " + blob);
    }

    [WorkItemValidator]
    public static void ProcessWorkItem(
        [QueueTrigger("test")] WorkItem workItem)
    {
        Console.WriteLine($"Processed work item {workItem.ID}");
    }
}

And here are the filter definitions:

public class WorkItemValidatorAttribute : FunctionInvocationFilterAttribute
{
    public override Task OnExecutingAsync(
        FunctionExecutingContext executingContext, CancellationToken cancellationToken)
    {
        executingContext.Logger.LogInformation("WorkItemValidator executing...");

        var workItem = executingContext.Arguments.First().Value as WorkItem;
        string errorMessage = null;
        if (!TryValidateWorkItem(workItem, out errorMessage))
        {
            executingContext.Logger.LogError(errorMessage);
            throw new ValidationException(errorMessage);
        }
                
        return base.OnExecutingAsync(executingContext, cancellationToken);
    }

    private static bool TryValidateWorkItem(WorkItem workItem, out string errorMessage)
    {
        errorMessage = null;

        if (string.IsNullOrEmpty(workItem.ID))
        {
            errorMessage = "ID cannot be null or empty.";
            return false;
        }
        if (workItem.Priority > 100 || workItem.Priority < 0)
        {
            errorMessage = "Priority must be between 0 and 100";
            return false;
        }

        return true;
    }
}

public class ErrorHandlerAttribute : FunctionExceptionFilterAttribute
{
    public override Task OnExceptionAsync(
        FunctionExceptionContext exceptionContext, CancellationToken cancellationToken)
    {
        // custom error handling logic could be written here
        // (e.g. write a queue message, send a notification, etc.)

        exceptionContext.Logger.LogError($"ErrorHandler called. Function '{exceptionContext.FunctionName}:{exceptionContext.FunctionInstanceId} failed.");

        return Task.CompletedTask;
    }
}
Clone this wiki locally