Middleware¶
The .NET isolated worker model has a first-class middleware pipeline. Middleware runs on every function invocation, wrapping the execution so you can add cross-cutting behavior — logging, authentication, exception handling, or response header stamping — in one place instead of repeating it in every function.
Prerequisites¶
- A .NET isolated worker Function App (
Microsoft.Azure.Functions.Worker.Sdk1.9.0 or later). - The app configured with
FunctionsApplication.CreateBuilderorHostBuilderinProgram.cs.
Architecture¶
flowchart TD
REQ[Trigger fires] --> M1[Middleware 1: pre-logic]
M1 --> M2[Middleware 2: pre-logic]
M2 --> FUNC[Function execution]
FUNC --> M2B[Middleware 2: post-logic]
M2B --> M1B[Middleware 1: post-logic]
M1B --> RESP[Response returned] Implement a Middleware¶
Middleware implements IFunctionsWorkerMiddleware from the Microsoft.Azure.Functions.Worker.Middleware namespace. Call next to continue the pipeline; code before next runs on the way in, code after next runs on the way out.
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Middleware;
using Microsoft.Extensions.Logging;
public sealed class ExceptionLoggingMiddleware : IFunctionsWorkerMiddleware
{
private readonly ILogger<ExceptionLoggingMiddleware> _logger;
public ExceptionLoggingMiddleware(ILogger<ExceptionLoggingMiddleware> logger)
{
_logger = logger;
}
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
_logger.LogInformation("Executing {Function}", context.FunctionDefinition.Name);
try
{
await next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled error in {Function}", context.FunctionDefinition.Name);
throw;
}
}
}
Constructor injection works because middleware is resolved from the DI container. See Dependency Injection.
Register the Pipeline¶
Register middleware in Program.cs. Order matters — the first registered runs outermost.
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Hosting;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(builder =>
{
builder.UseMiddleware<ExceptionLoggingMiddleware>();
builder.UseMiddleware<StampHeadersMiddleware>();
})
.Build();
host.Run();
Conditional Middleware¶
Use UseWhen to run middleware only when a predicate over the FunctionContext is true — for example, only for HTTP-triggered functions.
builder.UseWhen<StampHeadersMiddleware>((context) =>
{
// Run only when the function has an HTTP trigger.
return context.FunctionDefinition.InputBindings.Values
.Any(b => b.Type == "httpTrigger");
});
Access the HTTP Request and Response¶
Inside middleware you can read the HTTP request and write to the response through the invocation context.
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
await next(context);
var httpResponse = context.GetHttpResponseData();
httpResponse?.Headers.Add("x-processed-by", "functions-middleware");
}
| Element | Explanation |
|---|---|
IFunctionsWorkerMiddleware | Interface with a single Invoke(FunctionContext, FunctionExecutionDelegate) method. |
next(context) | Advances the pipeline; awaiting it runs the remaining middleware and the function. |
UseMiddleware<T>() | Registers middleware unconditionally, outermost-first. |
UseWhen<T>(predicate) | Registers middleware that runs only when the predicate is true. |
GetHttpResponseData() | Retrieves the HTTP response after next for header/status manipulation. |