DI for class not resolving in Function - c#

I have set up DI for an Azure function but it will not resolve when I run the function. The code I have is:
StartUp:
[assembly: FunctionsStartup(typeof(OmegaConnector.StartUp))]
namespace OmegaConnector
{
public class StartUp : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
builder.Services.AddLogging();
builder.Services.AddVehicleSearchCosmosDataProvider();
builder.Services.AddScoped<IProcessSearchData, SearchProcessor>(); <- This one
}
}
IProcessSearchData:
public interface IProcessSearchData
{
Task<bool> ProcessData(string campaign);
}
SearchProcessor:
public class SearchProcessor : IProcessSearchData
{
public async Task<bool> ProcessData(string campaign)
{
return true;
}
}
Function:
public OmegaConnectorFunction(ILogger<OmegaConnectorFunction> logger, IProcessSearchData searchProcessor)
{
I get the error:
Executed 'CatchCampaign' (Failed, Id=daef3371-fa4d-4d1f-abad-7ad343537872)
[27/05/2020 12:17:27] Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'OmegaConnector.Interfaces.IProcessSearchData' while attempting to activate 'OmegaConnector.OmegaConnectorFunction'.
Sorry if this is too simple but I just can't see what I have wrong here. I think I have this set up correctly but I obviously don't. Can anyone see what I need to do?

See here: https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library
The Functions 3.x packages are built with .NET Core 3.1 in mind.
Try keeping these versions in sync so there are no dependency compatibility problems.

From what I understood of the documentation provided by Microsoft the issue may be that the service needs to be injected into the class that contains the function.
I'm unsure if this is what you've done from the code examples you've provided. An example of this is:
public class OmegaConnectorFunction
{
private readonly ILogger _logger;
private readonly IProcessSearchData _searchProcessor;
public OmegaConnectorFunction(ILogger<OmegaConnectorFunction> logger, IProcessSearchData searchProcessor)
{
_logger = logger;
_searchProcessor = searchProcessor;
}
[FunctionName("OmegaConnectorFunction")]
public async Task<IActionResult> Run([HttpTrigger] HttpRequest request)
{
var campaign = await request.Content.ReadAsAsync<string>();
_searchProcessor.ProcessData(campaign);
return new OkResult();
}
}

public class OmegaConnectorFunction {
private readonly IProcessSearchData _searchProcessor;
public OmegaConnectorFunction(IProcessSearchData searchProcessor)
{
_searchProcessor = searchProcessor;
}
[FunctionName("OmegaConnectorFunction")]
public async Task<IActionResult> Run([HttpTrigger] HttpRequest request, ILogger log) // ILogger is automatically imported
{
var campaign = await request.Content.ReadAsAsync<string>();
_searchProcessor.ProcessData(campaign);
return new OkResult();
}
}

Related

Autofac Issue: Cannot resolve parameter of constructor 'Void .ctor

My .Net 7 application has the following issue:
Autofac.Core.DependencyResolutionException: An exception was thrown while activating MyApp.Modules.MyModule.Application.MyModule.UpdateCommand.UpdateCommandHandler.
---> Autofac.Core.DependencyResolutionException: None of the constructors found with 'MyApp.Modules.MyModule.Infrastructure.Configuration.AllConstructorFinder' on type 'MyApp.Modules.MyModule.Application.MyModule.UpdateCommand.UpdateCommandHandler' can be invoked with the available services and parameters:
Cannot resolve parameter 'MyApp.Modules.MyModule.Application.Contracts.IMyModule myModule' of constructor 'Void .ctor(Serilog.ILogger, MyApp.Modules.MyModule.Application.Contracts.IMyModule)'.
UpdateCommandHandler.cs (where the issue is occurring)
public class UpdateCommandHandler: ICommandHandler<UpdateCommand>
{
private readonly IMyModule _myModule;
private readonly ILogger _logger;
public UpdateCommandHandler(ILogger logger, IMyModule myModule)
{
_myModule = myModule;
_logger = logger;
}
public async Task<Unit> Handle(UpdateCommand request, CancellationToken cancellationToken)
{
var foo = await _myModule.ExecuteQueryAsync(new SampleQuery());
return Unit.Value;
}
}
Program.cs
...
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(b => b.RegisterModule(new AutofacModules()));
...
I looked at similar issues before posting, such as this, but I do believe I appropriately registered IMyModule in Autofac as MyModule in the following.
AutofacModules.cs
public class AutofacModules: Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<MyModule>().As<IMyModule>().InstancePerLifetimeScope();
}
}
IMyModule.cs
public interface IMyModule
{
Task ExecuteCommandAsync(ICommand command);
Task<TResult> ExecuteQueryAsync<TResult>(IQuery<TResult> query);
}
MyModule.cs
public class MyModule: IMyModule
{
public async Task ExecuteCommandAsync(ICommand command)
{
await CommandsExecutor.Execute(command);
}
public Task<TResult> ExecuteQueryAsync<TResult>(IQuery<TResult> query)
{
var scope = MyCompositionRoot.BeginLifetimeScope();
var mediator = scope.Resolve<IMediator>();
return mediator.Send(query);
}
}
AllConstructorFinder.cs
internal class AllConstructorFinder : IConstructorFinder
{
private static readonly ConcurrentDictionary<Type, ConstructorInfo[]> Cache = new();
public ConstructorInfo[] FindConstructors(Type targetType)
{
var result = Cache.GetOrAdd(targetType, t => t.GetTypeInfo().DeclaredConstructors.ToArray());
return result.Length > 0 ? result : throw new NoConstructorsFoundException(targetType);
}
}
In my Program.cs, I had registered MyModule, but, as I have multiple modules with their own containers, I didn't register it in the module's own composition root. By adding the following line, I'm able to include MyModule as a constructor parameter.
MyModuleStartup.cs
...
var containerBuilder = new ContainerBuilder();
...
/* NEW LINE */
containerBuilder.RegisterType<CventModule>().As<ICventModule>().InstancePerLifetimeScope();
...
So lesson here is make sure the component you are using is registered to Autofac root container your module is running directly in. Thanks to #Travis Illig for the troubleshooting link which helped me immensely.

Getting ILogger to write to the console from a service

I have a simple function app that uses MediatR pattern and the base function looks like this:
public class ShareEnvelopesFunction
{
private readonly IMediator _mediator;
public ShareEnvelopesFunction(IMediator mediator)
{
_mediator = mediator;
}
[FunctionName(nameof(ShareEnvelopesFunction))]
public async Task Run([TimerTrigger("%ScheduleShareEnvelopesSetting%")]TimerInfo timer, ILogger log)
{
log.LogInformation("Starting Share Envelopes function {0}", timer);
var result = await _mediator.Send(new ShareEnvelopesCommandRequest { log = log });
}
}
As you can see ShareEnvelopesCommandRequest is a class that has only one property which is the ILogger
public class ShareEnvelopesCommandRequest : IRequest<ShareEnvelopesCommandResponse>
{
public ILogger log { get; set; }
}
Now in my command handler, if I use request.log.LogInformation, it logs messages to my console. A sample command handler code looks like this:
public class ShareEnvelopesCommandHandler : IRequestHandler<ShareEnvelopesCommandRequest, ShareEnvelopesCommandResponse>
{
private readonly IUnitOfWork _unitOfWork;
private readonly IDocuSignApiService _docuSignService;
public ShareEnvelopesCommandHandler(IUnitOfWork unitOfWork
, IDocuSignApiService docuSignService
)
{
_unitOfWork = unitOfWork;
_docuSignService = docuSignService;
}
public async Task<ShareEnvelopesCommandResponse> Handle(ShareEnvelopesCommandRequest request, CancellationToken cancellationToken)
{
request.log.LogInformation("Starting to share envelopes");
await _docuSignService.ShareEnvelopes(fromGroupUser.UserId, deltaUsers);
return new ShareEnvelopesCommandResponse() {isSuccess=true };
}
Now the real issue here is that, if you see the above code, I am injecting a docusign service and inside this service, I need to log certain information into the console. My sample docusign service looks like this:
public class DocuSignApiService : IDocuSignApiService
{
public IGroupsApi _groupsApi { get; set; }
public IAccountsApi _accountsApi { get; set; }
public DocuSignApiService(IGroupsApi groupsApi, IAccountsApi accountsApi)
{
_groupsApi = groupsApi;
_accountsApi = accountsApi;
}
public async Task ShareEnvelopes(string fromUserId, List<string> toUsersList)
{
//_logger.LogInformation("This is a test");
}
}
Now I need to be able to log any information from this service to the console. Now I can pass the ShareEnvelopesCommandRequest request to this service but I don't think that would be very efficient. So here is what I have tried:
I injected ILogger into the service:
public class DocuSignApiService : IDocuSignApiService
{
public IGroupsApi _groupsApi { get; set; }
public IAccountsApi _accountsApi { get; set; }
private readonly ILogger _logger;
public DocuSignApiService(IGroupsApi groupsApi, IAccountsApi accountsApi, ILogger<ShareEnvelopesCommandRequest> logger )
{
_groupsApi = groupsApi;
_accountsApi = accountsApi;
_logger = logger;
}
public async Task ShareEnvelopes(string fromUserId, List<string> toUsersList)
{
_logger.LogInformation("This is a test");
}
}
The configuration on DI in my startup class looked like this:
services.AddScoped<IDocuSignApiService>(_ => new DocuSignApiService(docuSignConfig,
_.GetService<IGroupsApi>(),
_.GetService<IAccountsApi>(),
_.GetService<ILogger<ShareEnvelopesCommandRequest>>()
)
);
However, doing that didn't log information into the azure function app console. Are there an ideas on how I can go about logging messages from a service into the console? Thanks in advance.
Maybe you have to setup log level in host.json like this:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
},
"logLevel": {
"YourNamespace.DocuSignApiService": "Information",
"YourNamespace.ShareEnvelopesFunction": "Information",
}
}
}
I'm guessing you get the ILogger instance in your Run method directly from azure, whenever you run the function.
_.GetService<ILogger<ShareEnvelopesCommandRequest>>()
That above probably gets another instance unrelated to the one azure provides you and maybe that's why no logs happen.
If you're going to use DI, you gotta do it from the start of the Run method of your function class (ShareEnvelopesFunction).
public class ShareEnvelopesFunction
{
private readonly IMediator _mediator;
private readonly ServiceCollection _serviceCollection;
public ShareEnvelopesFunction(IMediator mediator, ServiceCollection serviceCollection)
{
_serviceCollection = serviceCollection;//inject this from your startup
_mediator = mediator;
}
[FunctionName(nameof(ShareEnvelopesFunction))]
public async Task Run([TimerTrigger("%ScheduleShareEnvelopesSetting%")] TimerInfo timer, ILogger log)
{
_serviceCollection.AddSingleton(log);//this should ensure that all log instances injected into your services are references to the one azure provided, or something like that
_serviceCollection.AddScoped<IDocuSignApiService,DocuSignApiService>();//now that your servicecollection knows what ILogger to inject, you can also inject your service normally, not sure if this needs to be done here tho, leaving it in your startup should be fine
log.LogInformation("Starting Share Envelopes function {0}", timer);
var result = await _mediator.Send(new ShareEnvelopesCommandRequest { log = log });
}
}
I've done this in a recent AWS Lambda project, their ILambdaContext also serve as logger. I was having the same issue as you, no logs in DI dependent services.
If that doesn't do the trick, try rebuilding your ServiceProvider before Mediator does it's thing.
using (IServiceProvider serviceProvider = _serviceCollection.BuildServiceProvider())
{
log.LogInformation("Starting Share Envelopes function {0}", timer);
_mediator = serviceProvider.GetService<IMediator>();
var result = await _mediator.Send(new ShareEnvelopesCommandRequest { log = log });
}

Dependecy Injection doesn't work with Azure Functions

I have a problem with dependecy injection in Azure Functions. I read every guide and any issue here on stackoverflow but I can't found a solution.
I'm working with Visual Studio 2019 and Azurite for testing in my local machine. I tried to make a project without and It works fine.
The project is an Azure Functions with HttpTrigger.
Here the link to my github repository
I paste here my Startup's code:
[assembly: FunctionsStartup(typeof(Startup))]
namespace ZanettiClod.SampleAzureFunctions
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton<IProductService, ProductService>();
builder.Services.AddSingleton<IProductRepository<Product>, ProductRepository>();
}
}
}
And my Program's code:
namespace ZanettiClod.SampleAzureFunctions
{
public class Program
{
public static void Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.Build();
host.Run();
}
}
}
My GetProducts's Code:
namespace ZanettiClod.SampleAzureFunctions
{
public class GetProducts
{
private readonly IProductService _productService;
public GetProducts(IProductService productService)
{
_productService = productService;
}
[Function("GetProducts")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")]
HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("GetProducts");
logger.LogInformation("C# HTTP trigger function processed a request.");
var products = await _productService.GetAllProducts();
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(products);
return response;
}
}
}
And here is the error I get back:
screeshot
Thanks in advance for the help
You could make below changes to make it work. I tested for Dependency injection and it worked.
Move dependency injection from startup.cs to program.cs. That's how it works for target framework .Net 5.0.
Documentation - https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide
namespace ZanettiClod.SampleAzureFunctions
{
public class Program
{
public static void Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(s =>
{
s.AddHttpClient();
s.AddSingleton<IProductService, ProductService>();
s.AddSingleton<IProductRepository<Product>, ProductRepository>();
})
.Build();
host.Run();
}
}
}
Change the qualifier for _productService from static to readonly in CreateProduct class. Dependency injection doesn't work on static member variables. Also remove static qualifier from your function.
public class CreateProduct
{
private readonly IProductService _productService;
public CreateProduct(IProductService productService)
{
_productService = productService;
}
[Function("CreateProduct")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("CreateProduct");
logger.LogInformation("C# HTTP trigger function processed a request.");
var product = await req.ReadFromJsonAsync<Product>();
_productService.CreateProduct(product);
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(
new
{
Result = true,
Message = $"Name: {product.Name}, Price: {product.Price}"
});
return response;
}
}
}

use mediator cqrs in TDD for integration test .net core 3

i use Mediator in my project .
Demo Project on Github
i want to use TDD in my project and integration test with .Net core 3.0
i write this code int test class for use intergration test with mediator :
public class SubscribeTest : IClassFixture<TravelRequest<Startup>>, IClassFixture<DbContextFactory>, IDisposable
{
private readonly TravelRequest<Startup> request;
private readonly DbContextFactory contextFactory;
public SubscribeTest(TravelRequest<Startup> request , DbContextFactory contextFactory)
{
this.request = request;
this.contextFactory = contextFactory;
}
public void Dispose()
{
request.Dispose();
}
[Fact]
public async Task ListSubscribeAsync()
{
var add = await request.Get("/Subscribe/GetListSubscribe");
await add.BodyAs<SubscribListDto>();
}
}
and this is TravelRequest :
public class TravelRequest<TStartup> : IDisposable where TStartup : class
{
private readonly HttpClient client;
private readonly TestServer server;
public TravelRequest()
{
var webHostBuilder = new WebHostBuilder().UseStartup<TStartup>().UseConfiguration(ConfigorationSingltonConfigoration.GetConfiguration());
this.server = new TestServer(webHostBuilder);
this.client = server.CreateClient();
}
}
and this is ConfigorationSingltonConfigoration for use the appSetting-test.json :
public class ConfigorationSingltonConfigoration
{
private static IConfigurationRoot configuration;
private ConfigorationSingltonConfigoration() { }
public static IConfigurationRoot GetConfiguration()
{
if (configuration is null)
configuration = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Path.GetFullPath("../../../")))
.AddJsonFile("appsettings-test.json")
.AddEnvironmentVariables()
.Build();
return configuration;
}
}
and finally this is for set DbContext :
public class DbContextFactory : IDisposable
{
public TravelContext Context { get; private set; }
public DbContextFactory()
{
var dbBuilder = GetContextBuilderOptions<TravelContext>("SqlServer");
Context = new TravelContext(dbBuilder.Options);
Context.Database.Migrate();
}
public void Dispose()
{
Context.Dispose();
}
public TravelContext GetRefreshContext()
{
var dbBuilder = GetContextBuilderOptions<TravelContext>("SqlServer");
Context = new TravelContext(dbBuilder.Options);
return Context;
}
private DbContextOptionsBuilder<TravelContext> GetContextBuilderOptions<T>(string connectionStringName)
{
var connectionString = ConfigorationSingltonConfigoration.GetConfiguration().GetConnectionString(connectionStringName);
var contextBuilder = new DbContextOptionsBuilder<TravelContext>();
var servicesCollection = new ServiceCollection().AddEntityFrameworkSqlServer().BuildServiceProvider();
contextBuilder.UseSqlServer(connectionString).UseInternalServiceProvider(servicesCollection);
return contextBuilder;
}
}
Now my problem is here , when i RunTest in result it show me this error :
---- System.InvalidOperationException : Unable to resolve service for type 'System.Collections.Generic.IList1[FluentValidation.IValidator1[Travel.Services.SubscribeServices.Query.SubscribeList.SubscribeListCommand]]' while attempting to activate 'Travel.Common.ValidateBehavior.ValidateCommandBehavior2[Travel.Services.SubscribeServices.Query.SubscribeList.SubscribeListCommand,Travel.Common.Operation.OperationResult1[System.Collections.Generic.IEnumerable`1[Travel.ViewModel.SubscibeDto.SubscribListDto]]]'. ---- System.NotImplementedException : The method or operation is not implemented.
whats the problem ? how can i solve ths problem ??? i put the source code of project in top of question
I suspect that the validators are not registered into the container.
You can use the FluentValidation in .NET Core with Dependency Injection, by installing the nuget and than passing the assembly.
More references at Using FluentValidation in .NET Core with Dependency Injection.
More reference of how you can do this at Fluent Validation on .NET Core 2.0 does not register.

How To Register Services For Custom .Net Core Middleware

I'm currently trying to understand and work with custom middleware in DotNet Core.
According to the Microsoft docs:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1
Middleware should follow the Explicit Dependencies Principle by exposing its dependencies in its constructor.
So if I follow this principle, I end up with something like this
Explicit Dependency Version
public static class ApplicationBuilderFeatureHeaderExtension
{
public static IApplicationBuilder UseFeatureHeaders(this IApplicationBuilder app, Action<FeaturePolicyStringBuilder> builder)
{
return app.UseMiddleware<FeaturePolicyHeaderMiddleware>(builder);
}
}
public class FeaturePolicyHeaderMiddleware
{
private RequestDelegate _next;
private Action<FeaturePolicyStringBuilder> _builder;
private FeaturePolicyStringBuilder _builderInstance;
public FeaturePolicyHeaderMiddleware(RequestDelegate next, Action<FeaturePolicyStringBuilder> builder, FeaturePolicyStringBuilder builderInstance)
{
_next = next;
_builderInstance = builderInstance;
_builder = builder;
_builder(_builderInstance);
}
public async Task Invoke(HttpContext context)
{
var header = _builderInstance.CreateFeaturePolicyHeader();
if (!context.Response.Headers.ContainsKey(header.Key))
{
context.Response.Headers.Add(_builderInstance.CreateFeaturePolicyHeader());
}
await _next.Invoke(context);
}
}
Here the FeaturePolicyStringBuilder is being provided as a service and has been registered in the startup file.
The Other Version (Using New)
public class FeaturePolicyHeaderMiddleware
{
private RequestDelegate _next;
private Action<FeaturePolicyStringBuilder> _builder;
private FeaturePolicyStringBuilder _builderInstance;
public FeaturePolicyHeaderMiddleware(RequestDelegate next, Action<FeaturePolicyStringBuilder> builder)
{
_next = next;
_builderInstance = new FeaturePolicyStringBuilder();
_builder = builder;
_builder(_builderInstance);
}
public async Task Invoke(HttpContext context)
{
var header = _builderInstance.CreateFeaturePolicyHeader();
if (!context.Response.Headers.ContainsKey(header.Key))
{
context.Response.Headers.Add(_builderInstance.CreateFeaturePolicyHeader());
}
await _next.Invoke(context);
}
}
In this version I am simply "Newing Up" the dependency, meaning that I don't have to register it as a service, this makes it easier to encapsulate into it's own project.
(Im not sure really how evil this is a it seems to me I'm already tightly coupled to the FeaturePolicyStringBuilder due to the Action requiring it as a type, so why not more glue!)
The Question
Is there a way to still follow the Explicit Dependencies Principle while not having to explicitly register those dependencies with the Service Provider? or some way to register them within the middlware component itself?
Thanks!
P.S: I have added the builder code below so that the purpose of the code is more clear: as per gerry.inc's comment
public class FeaturePolicyStringBuilder
{
private string _featurePolicyString;
public KeyValuePair<string, StringValues> CreateFeaturePolicyHeader()
{
return new KeyValuePair<string, StringValues>("feature-policy", _featurePolicyString);
}
private void CreateDirective(string directiveName, Action<SourceBuilder> builder)
{
var builderObj = new SourceBuilder();
builder(builderObj);
_featurePolicyString += $"{directiveName} '{builderObj.GetPrefix()}' {builderObj.GetSources()};";
}
public FeaturePolicyStringBuilder Camera(Action<SourceBuilder> builder)
{
CreateDirective("camera", builder);
return this;
}
public FeaturePolicyStringBuilder Accelerometer(Action<SourceBuilder> builder)
{
CreateDirective("accelerometer", builder);
return this;
}
public FeaturePolicyStringBuilder Battery(Action<SourceBuilder> builder)
{
CreateDirective("battery", builder);
return this;
}
}
What the call in the Configure method looks like
app.UseFeatureHeaders(x =>
x.Camera(b =>
b.AddPrefix(HeaderPrefixEnum.HeaderPrefix.Self)
.AddSource("Test"))
.Accelerometer(b =>
b.AddPrefix(HeaderPrefixEnum.HeaderPrefix.Self)
.AddSource("Test")
.AddSource("Test")
.AddPrefix(HeaderPrefixEnum.HeaderPrefix.None)
.AddPrefix(HeaderPrefixEnum.HeaderPrefix.Src)));
Given the way the builder is being configured, then the builder and configuration action should be invoked before adding the middleware
public static class ApplicationBuilderFeatureHeaderExtension {
public static IApplicationBuilder UseFeatureHeaders(this IApplicationBuilder app, Action<FeaturePolicyStringBuilder> configureBuilder) {
FeaturePolicyStringBuilder builder = new FeaturePolicyStringBuilder();
configureBuilder?.Invoke(builder);
var header = builder.CreateFeaturePolicyHeader();
return app.UseMiddleware<FeaturePolicyHeaderMiddleware>(header);
}
}
And the middleware refactored accordingly
public class FeaturePolicyHeaderMiddleware {
private RequestDelegate _next;
private KeyValuePair<string, StringValues> header;
public FeaturePolicyHeaderMiddleware(RequestDelegate next, KeyValuePair<string, StringValues> header) {
_next = next;
this.header = header;
}
public async Task Invoke(HttpContext context) {
if (!context.Response.Headers.ContainsKey(header.Key)) {
context.Response.Headers.Add(header);
}
await _next.Invoke(context);
}
}
This DRY approach allows for better separation of concerns and explicitly indicates what the middleware actual needs to perform its function.

Categories

Resources