Skip to content

Releases: Cloud-Jas/AzureFunctions.Extensions.Middleware

v4.0.1

31 Aug 20:33
9eae692

Choose a tag to compare

  • Unified Middleware for both HTTP and Non-HTTP triggers in In-process & Isolated process mode in Azure Functions
  • Added support for .NET 8.0 Isolated Process

v4.0

31 Aug 20:18

Choose a tag to compare

  • Unified Middleware for both HTTP and Non-HTTP triggers in In-process & Isolated process mode in Azure Functions
  • Added support for .NET 8.0 Isolated Process

v3.0

20 Jul 06:00

Choose a tag to compare

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

v2.1.0

21 Jun 19:41

Choose a tag to compare

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!

Sponsor

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

Buy Me A Coffee

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

v2.0.1

09 Jun 08:46
abe5426

Choose a tag to compare

We supported handling cross cutting concerns for non-http triggers using TaskMiddleware

UPDATE:

Sample Timer Trigger:

[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");
}));
}

v2.0.0

09 Jun 08:35

Choose a tag to compare

We supported handling cross cutting concerns for non-http triggers using TaskMiddleware

UPDATE:

Sample Timer Trigger:

[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");
}));
}

v1.0.2

16 Jan 20:49
4a5394d

Choose a tag to compare

  • Updated Tag Version in Build task
  • Automated NuGet publishing

v1.0.1

16 Jan 20:37

Choose a tag to compare

  • Able to add multiple custom middleware to the pipeline
  • Able to access HTTP context inside the custom middleware
  • Able to bypass middleware and return response
  • Handle Crosscutting concerns of the application
    • Logging
    • Exception Handling
    • CORS
    • Performance Monitoring
    • Caching
    • Security
  • Licensed under MIT - 100% free for personal and commercial use

Full Changelog: https://github.com/Cloud-Jas/AzureFunctions.Extensions.Middleware/commits/1.0.1