# Middleware support for Azure Functions v3.0 ## About this blog Hey folks!, In this blog we will see about a library [AzureFunctions.Extensions.Middleware](https://github.com/Cloud-Jas/AzureFunctions.Extensions.Middleware) that I developed to make use of middleware pattern to address some of the cross-cutting concerns of our applications. ------------------- ## Middleware support in Azure Functions Historically we always have known .NET Azure Functions have been in the in-process mode. Until the release of .NET 5, now there are two modes in which we can run Azure Functions - Out-of-process([Isolated](https://docs.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide)) - In-process In the out-of-process mode we have native support for middleware as per the design ( we have direct control over the execution of our function app, which runs as a separate process). But in the In-Process mode there is no middleware capability as we don't have full control over the application's startup and dependencies it consumes. Find more details [here](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/net-on-azure-functions-roadmap/ba-p/2197916) ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i63738y3eut7v8tx9hka.png) ------------------- ## AzureFunctions.Extensions.Middleware
🛑 It is now obvious that we won't be having a direct way to access the pipeline of execution in the traditional In-Process mode which most of us are using in the production environments.
## Updates 3.0 * Separated concerns of Http and non-http triggers * bug-fixes in accessing executionContext * cleaner approach to access data for non-http triggers > Note: Breaking change of class name changes > * FunctionsMiddleware => HttpMiddleware > * TaskMiddleware => NonHttpMiddleware > * IMiddlewareBuilder => IHttpMiddlewareBuilder > * ServerlessMiddleware => HttpMiddlewareBase ## Features * Able to add multiple custom middlewares to the pipeline * Able to access HTTP context inside the custom middleware * Able to access ExecutionContext & data inside non-http triggers * Able to inject middlewares in all the triggers available * Able to bypass middlewares and return response * Handle Crosscutting concerns of the application * Logging * Exception Handling * CORS * Performance Monitoring * Caching * Security * OpenTelemetry * Licenced under MIT - 100% free for personal and commercial use ## Supported Frameworks - NetCoreApp 3.1 - NET 5.0 - NET 6.0

(back to top)

## Installation ### Install with Package Manager Console `PM> Install-Package AzureFunctions.Extensions.Middleware` ## Usage ### Getting Started # 1. HTTP Triggers ## 1.1 Add HttpContextAccessor in Startup.cs Inorder to access/modify HttpContext within custom middleware we need to inject HttpContextAccessor to DI in Startup.cs file ```cs builder.Services.AddHttpContextAccessor() ``` ## 1.2. Add custom middlewares to the pipeline in Startup.cs One or more custom middlewares can be added to the execution pipeline as below. ```cs builder.Services.AddTransient((serviceProvider) => { var funcBuilder = new HttpMiddlewareBuilder(serviceProvider.GetRequiredService()); funcBuilder.Use(new ExceptionHandlingMiddleware(serviceProvider.GetService>())); funcBuilder.UseWhen(ctx => ctx != null && ctx.Request.Path.StartsWithSegments("/api/Authorize"), new AuthorizationMiddleware(serviceProvider.GetService>())); return funcBuilder; }); ``` ### 1.2.1 Use() - Use() middleware takes custom middleware as parameter and will be applied to all the endpoints ### 1.2.2 UseWhen() - UseWhen() takes Func and custom middleware as parameters. If the condition is satisfied then middleware will be added to the pipeline of exectuion. ## 1.3. Pass IHttpMiddlewareBuilder in Http trigger class Pass IHttpMiddlewareBuilder dependency to the constructor of Http trigger class ```cs private readonly ILogger _logger; private readonly IHttpMiddlewareBuilder _middlewareBuilder; public FxDefault(ILogger log, IHttpMiddlewareBuilder middlewareBuilder) { _logger = log; _middlewareBuilder = middlewareBuilder; } ``` ## 1.4. Modify http triggers methods All of our custom middlewares are added in the Startup.cs file and now we need to bind last middleware for our HttpTrigger method , use "_middlewareBuilder.ExecuteAsync(new HttpMiddleware(async (httpContext) =>{HTTP trigger code},executionContext)" to wrap the code. > NOTE: pass optional parameter {executionContext} to use it in the custom middlewares , refer 1.5 to see how to make use of executionContext ```cs public class FxDefault { private readonly ILogger _logger; private readonly IHttpMiddlewareBuilder _middlewareBuilder; public FxDefault(ILogger log, IHttpMiddlewareBuilder middlewareBuilder) { _logger = log; _middlewareBuilder = middlewareBuilder; } [FunctionName("Function1")] [OpenApiOperation(operationId: "Run", tags: new[] { "name" })] [OpenApiParameter(name: "name", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **Name** parameter")] [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "text/plain", bodyType: typeof(string), Description = "The OK response")] public async Task Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,ExecutionContext executionContext) { return await _middlewareBuilder.ExecuteAsync(new Extensions.Middleware.HttpMiddleware(async (httpContext) => { _logger.LogInformation("C# HTTP trigger default function processed a request."); string name = httpContext.Request.Query["name"]; string requestBody = await new StreamReader(httpContext.Request.Body).ReadToEndAsync(); dynamic data = JsonConvert.DeserializeObject(requestBody); name = name ?? data?.name; string responseMessage = string.IsNullOrEmpty(name) ? "This HTTP triggered default function executed successfully. Pass a name in the query string or in the request body for a personalized response." : $"Hello, {name}. This HTTP triggered default function executed successfully."; return new OkObjectResult(responseMessage); }, executionContext)); } } ``` In the above example we have passed executionContext as parameter to HttpMiddleware. This will be made available to all the custom middleware that are registered in startup.cs ## 1.5 How to define Custom middlewares for http triggers? All custom middleware of Http triggers should inherit from HttpMiddlewareBase and override InvokeAsync method . You will be able to access both HttpContext and ExecutionContext > Note > You have access to execution context in all the custom middlewares , only if you pass the executionContext as 2nd parameter in the HttpMiddleware wrapper (refer 1.4) > To access it use {this.ExecutionContext} , refer below ```cs public class ExceptionHandlingMiddleware : HttpMiddlewareBase { private readonly ILogger _logger; public ExceptionHandlingMiddleware(ILogger logger) { _logger = logger; } public override async Task InvokeAsync(HttpContext context) { try { _logger.LogInformation($"{this.ExecutionContext.FunctionName} Request triggered"); await this.Next.InvokeAsync(context); _logger.LogInformation($"{this.ExecutionContext.FunctionName} Request processed without any exceptions"); } catch (Exception ex) { _logger.LogError(ex.Message); context.Response.StatusCode = 400; await context.Response.WriteAsync($"{this.ExecutionContext.FunctionName} request failed, Please try again"); } } } ``` # 2. Non-HTTP Triggers ## 2.1 Add custom middlewares to the pipeline in Startup.cs One or more custom middlewares can be added to the execution pipeline as below for non-http triggers ```cs builder.Services.AddTransient((serviceProvider) => { var funcBuilder = new NonHttpMiddlewareBuilder(); funcBuilder.Use(new TaskExceptionHandlingMiddleware(serviceProvider.GetService>())); funcBuilder.Use(new TimerDataAccessMiddleware(serviceProvider.GetService>())); return funcBuilder; }); ``` ### 2.1.1 Use() - Use() middleware takes custom middleware as parameter and will be applied to all the endpoints > NOTE: UseWhen is not available in non-http triggers
> However you could use ExecutionContext in each custom middleware to perform similar logic :). Refer the examples given below ## 2.2. Pass INonHttpMiddlewareBuilder in Non-Http trigger class Pass INonHttpMiddlewareBuilder dependency to the constructor of Non-Http trigger class ```cs private readonly ILogger _logger; private readonly INonHttpMiddlewareBuilder _middlewareBuilder; public TimerTrigger(ILogger log, INonHttpMiddlewareBuilder middlewareBuilder) { _logger = log; _middlewareBuilder = middlewareBuilder; } ``` ## 2.3. Modify non-http triggers methods All of our custom middlewares are added in the Startup.cs file and now we need to bind last middleware for our HttpTrigger method , use "_middlewareBuilder.ExecuteAsync(new NonHttpMiddleware(async (httpContext) =>{HTTP trigger code},executionContext,data)" to wrap the code. > NOTE: pass optional parameters {executionContext,data} to use it in the custom middlewares , refer 1.5 to see how to make use of executionContext ```cs public class TimerTrigger { private readonly ILogger _logger; private readonly INonHttpMiddlewareBuilder _middlewareBuilder; public TimerTrigger(ILogger log, INonHttpMiddlewareBuilder middlewareBuilder) { _logger = log; _middlewareBuilder = middlewareBuilder; } [FunctionName("TimerTrigger")] public async Task Run([TimerTrigger("*/10 * * * * *")] TimerInfo myTimer, ILogger log,ExecutionContext context) { await _middlewareBuilder.ExecuteAsync(new NonHttpMiddleware(async () => { _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); await Task.FromResult(true); },context,myTimer)); } } ``` In the above example we have passed both executionContext and timerinfo data as parameters to NonHttpMiddleware. This will be made available to all the custom middleware that are registered in startup.cs ## 2.4 How to define Custom middlewares for non-http triggers? All custom middleware of Non-Http triggers should inherit from TaskMiddleware and override InvokeAsync method . You will be able to access both ExecutionContext and Data > Note > You have access to execution context and data in all the custom middlewares , only if you pass the executionContext and data as 2nd,3rd parameter in the NonHttpMiddleware wrapper respectively (refer 1.4) > To access it use {this.ExecutionContext}/{this.Data} , refer below ```cs public class TimerDataAccessMiddleware : NonHttpMiddlewareBase { private readonly ILogger _logger; public TimerDataAccessMiddleware(ILogger logger) { _logger = logger; } public override async Task InvokeAsync() { if (this.ExecutionContext.FunctionName.Equals("TimerTrigger")) { try { var timerData = this.Data as TimerInfo; _logger.LogInformation($"{this.ExecutionContext.FunctionName} Request triggered"); await this.Next.InvokeAsync(); _logger.LogInformation($"{this.ExecutionContext.FunctionName} Request processed without any exceptions"); } catch (Exception ex) { _logger.LogError(ex.Message); } } await this.Next.InvokeAsync(); } } ``` ## How to contribute? If you wish to contribute to this open source or provide feedback, Feel free to reach out to me on below links - [LinkedIn](https://www.linkedin.com/in/Divakar-Kumar) - [GitHub](https://www.github.com/Divakar-Kumar) - [Discord](https://discord.gg/QTeHPYT3) ------------- ## Github Source code Leave a ⭐ in the below github repo if this library helped you at handling cross-cutting concerns of the application. https://github.com/Cloud-Jas/AzureFunctions.Extensions.Middleware ------------------- ## Special thanks - [Sriram](https://www.linkedin.com/in/sriram-ganesan-it/) ------------------- ## Join us Please go ahead and join our discord channel (https://discord.gg/8Cs82yNS) to give some valuable feedbacks ## Sponsor