AzureFunctions.Extensions.Middleware 2.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package AzureFunctions.Extensions.Middleware --version 2.1.0
NuGet\Install-Package AzureFunctions.Extensions.Middleware -Version 2.1.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AzureFunctions.Extensions.Middleware" Version="2.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add AzureFunctions.Extensions.Middleware --version 2.1.0
#r "nuget: AzureFunctions.Extensions.Middleware, 2.1.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install AzureFunctions.Extensions.Middleware as a Cake Addin
#addin nuget:?package=AzureFunctions.Extensions.Middleware&version=2.1.0

// Install AzureFunctions.Extensions.Middleware as a Cake Tool
#tool nuget:?package=AzureFunctions.Extensions.Middleware&version=2.1.0

Features

  • Able to add multiple custom middlewares to the pipeline
  • Able to access HTTP context inside the custom middleware
  • Able to access ExecutionContext 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
  • Licenced under MIT - 100% free for personal and commercial use

Supported Frameworks

  • NetCoreApp 3.1
  • NET 5.0
  • NET 6.0

Installation

Install with Package Manager Console

PM> Install-Package AzureFunctions.Extensions.Middleware

Usage

Getting Started

1. Add HttpContextAccessor and ExecutionContext to service collection

Inorder to access/modify HttpContext within custom middleware we need to add HttpContextAccessor in Startup.cs file and for accessing ExecutionContext we need to add FunctionExecutionContext concrete class in Startup.cs


builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<IExecutionContext, FunctionExecutionContext>();

2. Add custom middlewares to the pipeline

One or more custom middlewares can be added to the execution pipeline using MiddlewareBuilder.


builder.Services.AddTransient<IMiddlewareBuilder, MiddlewareBuilder>((serviceProvider) =>
            {
				// added httpcontextaccessor and executioncontext to middlewarebuilder
                var funcBuilder = new MiddlewareBuilder(serviceProvider.GetRequiredService<IHttpContextAccessor>(),serviceProvider.GetRequiredService<IExecutionContext>());
				
				//add custom middlewares to the execution pipeline
                funcBuilder.Use(new ExceptionHandlingMiddleware(new LoggerFactory().CreateLogger(nameof(ExceptionHandlingMiddleware))));
				
				// add custom middleware based on condition (works in HTTP trigger)
                funcBuilder.UseWhen(ctx => ctx.Request.Path.StartsWithSegments("/api/Authorize"),
                    new AuthorizationMiddleware(new LoggerFactory().CreateLogger(nameof(AuthorizationMiddleware))));
                
				return funcBuilder;
            });

2.1 Use()

  • Use() middleware takes custom middleware as parameter and will be applied to all the endpoints

2.2 UseWhen()

  • UseWhen() takes Func<HttpContext, bool> and custom middleware as parameters. If the condition is satisfied then middleware will be added to the pipeline of exectuion.

3. IMiddlewareBuilder dependency

We can now add IMiddlewareBuilder as a dependency to our HTTP trigger function class.


        private readonly ILogger<Function1> _logger;
        private readonly IMiddlewareBuilder _middlewareBuilder;

        public Function1(ILogger<Function1> log, IMiddlewareBuilder middlewareBuilder)
        {
            _logger = log;            
            _middlewareBuilder = middlewareBuilder;
        }

4. Define Custom middlewares

If HTTP trigger is used try to implement the InvokeAsync(HttpContext) and for non-http triggers implement InvokeAsync(ExecutionContext), (If both http and non-http triggers are deployed in same azure function try to implement both methods)


public class ExceptionHandlingMiddleware : ServerlessMiddleware
    {
        private readonly ILogger _logger;
        public ExceptionHandlingMiddleware(ILogger logger)
        {
            _logger = logger;
        }
        public override async Task InvokeAsync(HttpContext context)
        {
            try
            {
                _logger.LogInformation("Request triggered");

                await this.Next.InvokeAsync(context);

                _logger.LogInformation("Request processed without any exceptions");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);

                context.Response.StatusCode = 400;
                
                await context.Response.WriteAsync("Http Trigger request failed, Please try again");

            }
        }

      public override async Task InvokeAsync(ExecutionContext context)
      {
         try
         {
            _logger.LogInformation("Request triggered");

            await this.Next.InvokeAsync(context);

            _logger.LogInformation("Request processed without any exceptions");
         }
         catch (Exception ex)
         {
            _logger.LogError(ex.Message);           
         }
      }
   }

5. Execute pipeline

Now we need to bind last middleware for our HttpTrigger method , to do that wrap our existing code inside Functionsmiddleware block "_middlewareBuilder.ExecuteAsync(new FunctionsMiddleware(async (httpContext) ⇒{HTTP trigger code})"

For returning IActionResult use FunctionsMiddleware

 
return await _middlewareBuilder.ExecuteAsync(new FunctionsMiddleware(async (httpContext) =>
            {
                _logger.LogInformation("C# HTTP trigger 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 function executed successfully. Pass a name in the query string or in the request body for a personalized response."
                    : $"Hello, {name}. This HTTP triggered function executed successfully.";

                return new OkObjectResult(responseMessage);
            }));

For non-http triggers use TaskMiddleware


[FunctionName("TimerTrigger")]
       public async Task Run([TimerTrigger("*/10 * * * * *")] TimerInfo myTimer, ILogger log)
      {
         await _middlewareBuilder.ExecuteAsync(new TaskMiddleware(async (httpContext) =>
         {
            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
            await Task.FromResult("test");
         }));
      }	 	 

Based on the type of middleware(TaskMiddleware or FunctionsMiddleware) , respective InvokeAsync method will called with ExecutionContext or HttpContext

Sample

You can find .NET 6 sample application here . In this example we have registered Exception handling custom middleware to the exectuion order that will handle any unhandled exceptions in the Http Trigger execution.

Special Thanks

Thank you to the following people for their support and contributions!

Leave a ⭐ if this library helped you at handling cross-cutting concerns in serverless architecture.

<a href="https://www.buymeacoffee.com/divakarkumar" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 40px !important;width: 145 !important;" ></a>

Website | LinkedIn | Forum | Contribution Guide | Donate | License

© Divakar Kumar

For detailed documentation, please visit the docs.

Contact

Divakar Kumar - @Divakar-Kumar - https://iamdivakarkumar.com

Project Link: https://github.com/Cloud-Jas/AzureFunctions.Extensions.Middleware

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.1 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
3.0.0 32,744 7/20/2022
2.1.0 6,454 6/21/2022
2.0.1 420 6/9/2022
2.0.0 405 6/9/2022
1.0.2 1,696 1/16/2022
1.0.0 422 1/16/2022