如何为自定义 .Net Core 中间件注册服务

How To Register Services For Custom .Net Core Middleware

我目前正在尝试理解和使用 DotNet Core 中的自定义中间件。

根据 Microsoft 文档:

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.

所以如果我遵循这个原则,我最终会得到这样的结果

显式依赖版本

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

这里 FeaturePolicyStringBuilder 作为服务提供,并已在启动文件中注册。


其他版本(使用新)

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

在这个版本中我只是"Newing Up"依赖,意味着我不必将其注册为服务,这样更容易封装到它自己的项目中。
(我不确定这到底有多邪恶,在我看来我已经与 FeaturePolicyStringBuilder 紧密耦合,因为 Action 需要它作为一种类型,所以为什么不更多胶水!)

问题 有没有办法在不必向服务提供者显式注册这些依赖项的情况下仍然遵循显式依赖项原则?或者以某种方式在中间件组件本身中注册它们?

谢谢!

P.S:我在下面添加了生成器代码,以便代码的目的更加清晰:根据 gerry.inc 的评论

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;
    }
}

Configure 方法中的调用是什么样的

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)));

考虑到构建器的配置方式,应该在添加中间件之前调用构建器和配置操作

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

并对中间件进行了相应的重构

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

这种 DRY 方法允许更好地分离关注点并明确指示中间件实际需要什么来执行其功能。