![]() |
VOOZH | about |
dotnet add package AzureFunctions.Extensions.Middleware --version 4.0.1
NuGet\Install-Package AzureFunctions.Extensions.Middleware -Version 4.0.1
<PackageReference Include="AzureFunctions.Extensions.Middleware" Version="4.0.1" />
<PackageVersion Include="AzureFunctions.Extensions.Middleware" Version="4.0.1" />Directory.Packages.props
<PackageReference Include="AzureFunctions.Extensions.Middleware" />Project file
paket add AzureFunctions.Extensions.Middleware --version 4.0.1
#r "nuget: AzureFunctions.Extensions.Middleware, 4.0.1"
#:package AzureFunctions.Extensions.Middleware@4.0.1
#addin nuget:?package=AzureFunctions.Extensions.Middleware&version=4.0.1Install as a Cake Addin
#tool nuget:?package=AzureFunctions.Extensions.Middleware&version=4.0.1Install as a Cake Tool
<p align="right">(<a href="#top">back to top</a>)</p>
PM> Install-Package AzureFunctions.Extensions.Middleware
Inorder to access/modify HttpContext within custom middleware we need to inject HttpContextAccessor to DI in Startup.cs file
builder.Services.AddHttpContextAccessor()
We need to add FunctionContextAccessor to DI in Startup.cs file. Based on https://gist.github.com/dolphinspired/796d26ebe1237b78ee04a3bff0620ea0 FunctionContextAccessor is implemented to access the FunctionContext in Isolated Process mode.
new HostBuilder()
.ConfigureFunctionsWebApplication(applicationBuilder =>
{
applicationBuilder.UseFunctionContextAccessor();
})
.ConfigureServices(services =>
{
services.AddFunctionContextAccessor();
}
One or more custom middlewares can be added to the execution pipeline as below.
builder.Services.AddTransient<IHttpMiddlewareBuilder, HttpMiddlewareBuilder>((serviceProvider) =>
{
var funcBuilder = new HttpMiddlewareBuilder(serviceProvider.GetRequiredService<IHttpContextAccessor>());
funcBuilder.Use(new ExceptionHandlingMiddleware(serviceProvider.GetService<ILogger<ExceptionHandlingMiddleware>>()));
funcBuilder.UseWhen(ctx => ctx != null && ctx.Request.Path.StartsWithSegments("/api/Authorize"),
new AuthorizationMiddleware(serviceProvider.GetService<ILogger<AuthorizationMiddleware>>()));
return funcBuilder;
});
services.AddTransient<IHttpMiddlewareBuilder, HttpMiddlewareBuilder>((serviceProvider) =>
{
var funcBuilder = new HttpMiddlewareBuilder(serviceProvider.GetRequiredService<IFunctionContextAccessor>());
funcBuilder.Use(new ExceptionHandlingMiddleware(serviceProvider.GetService<ILogger<ExceptionHandlingMiddleware>>()));
funcBuilder.UseWhen(ctx =>
{
if (ctx != null && ctx.Request.Path.StartsWithSegments("/api/Authorize"))
{
return true;
}
return false;
}, new AuthorizationMiddleware(serviceProvider.GetService<ILogger<AuthorizationMiddleware>>()));
return funcBuilder;
});
})
Pass IHttpMiddlewareBuilder dependency to the constructor of Http trigger class
private readonly ILogger<FxDefault> _logger;
private readonly IHttpMiddlewareBuilder _middlewareBuilder;
public FxDefault(ILogger<FxDefault> log, IHttpMiddlewareBuilder middlewareBuilder)
{
_logger = log;
_middlewareBuilder = middlewareBuilder;
}
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
public class FxDefault
{
private readonly ILogger<FxDefault> _logger;
private readonly IHttpMiddlewareBuilder _middlewareBuilder;
public FxDefault(ILogger<FxDefault> 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<IActionResult> 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
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
public class ExceptionHandlingMiddleware : HttpMiddlewareBase
{
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(ILogger<ExceptionHandlingMiddleware> 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");
}
}
}
One or more custom middlewares can be added to the execution pipeline as below for non-http triggers
builder.Services.AddTransient<INonHttpMiddlewareBuilder, NonHttpMiddlewareBuilder>((serviceProvider) =>
{
var funcBuilder = new NonHttpMiddlewareBuilder();
funcBuilder.Use(new TaskExceptionHandlingMiddleware(serviceProvider.GetService<ILogger<TaskExceptionHandlingMiddleware>>()));
funcBuilder.Use(new TimerDataAccessMiddleware(serviceProvider.GetService<ILogger<TimerDataAccessMiddleware>>()));
return funcBuilder;
});
NOTE: UseWhen is not available in non-http triggers
<br>
However you could use ExecutionContext in each custom middleware to perform similar logic 😃. Refer the examples given below
Pass INonHttpMiddlewareBuilder dependency to the constructor of Non-Http trigger class
private readonly ILogger<TimerTrigger> _logger;
private readonly INonHttpMiddlewareBuilder _middlewareBuilder;
public TimerTrigger(ILogger<TimerTrigger> log, INonHttpMiddlewareBuilder middlewareBuilder)
{
_logger = log;
_middlewareBuilder = middlewareBuilder;
}
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
public class TimerTrigger
{
private readonly ILogger<TimerTrigger> _logger;
private readonly INonHttpMiddlewareBuilder _middlewareBuilder;
public TimerTrigger(ILogger<TimerTrigger> 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
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
public class TimerDataAccessMiddleware : NonHttpMiddlewareBase
{
private readonly ILogger<TimerDataAccessMiddleware> _logger;
public TimerDataAccessMiddleware(ILogger<TimerDataAccessMiddleware> 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();
}
}
You can find In-Process and Isolated sample application . 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.
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.
| LinkedIn | Forum | | Donate |
©
For detailed documentation, please visit the .
Divakar Kumar - @Divakar-Kumar - https://iamdivakarkumar.com
Project Link: https://github.com/Cloud-Jas/AzureFunctions.Extensions.Middleware
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 is compatible. net5.0-windows net5.0-windows was computed. net6.0 net6.0 is compatible. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp3.1 netcoreapp3.1 is compatible. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.