I'm currently trying to understand and work with custom middleware in DotNet Core.
According to the Microsoft docs:
https://docs.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)));
_builderInstance.CreateFeaturePolicyHeader()do? I assume it produces a key value pair to be added to response headers, but is it more than justreturn new Kvp("Header","Value")? Im tempted to think the options pattern can help, but im not so sure what exactly your code does. - Gerald Chifanzwa