使用 ASP.NET Core 中扩展方法的中间件配置

Using middleware config from extension methods in ASP.NET Core

我开发了一个ASP.NET核心中间件。我在这个 post 中简化了中间件,以使问题更清楚。

中间件在Startup.cs中配置如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...
    app.UseMyMiddleware(42);
    ...
}

整数(42)存储在中间件实例中,一切正常。

现在我想创建一些辅助方法作为扩展方法:

public static int Add(this int i)
{
    return i + TODO;
}

我想要将 i 的值添加到发送到中间件的值 (42) 的方法。

目前,我已将 42 的值保存在中间件内的 public 静态变量中,并通过我的扩展方法访问它:

public static int Add(this int i)
{
    return i + MyMiddleware.Value;
}

出于各种原因,我不喜欢这个解决方案。任何关于如何以更好的方式解决这个问题的想法将不胜感激。

你为什么不使用 appsettings.json 来存储这些信息,然后使用内置的 DI 将它注入你的中间件?

appsettings.json

{
    "MiddlewareConfiguration":
    {
        "MiddlewareValue":42,
    }
}

Startup.cs

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.Configure<MiddlewareConfiguration>(configuration.GetSection("MiddlewareConfiguration"));
}

MiddlewareConfiguration.cs

public class MiddlewareConfiguration
{
    public int MiddlewareValue { get; set; }
}

然后您可以将其注入您的中间件,例如:

public async Task Invoke(HttpContext context, IOptions<MiddlewareConfiguration> config)

一般来说,这不适用于扩展方法。扩展方法是静态的,因此只能访问其他静态成员。 ASP.NET 然而,核心 设计 不使用任何静态,并广泛使用 dependency injection 来管理对象的生命周期和访问依赖项。

所以通常,正确的方法是将您的扩展方法移动到某种非静态帮助程序对象中,然后您可以将其注入需要访问它的地方。当然,这不会让你在任何需要它的地方轻松调用它,而是需要你显式依赖它(这是依赖注入的要点之一)。

话虽如此,您的明确案例有点不同。正如我从你的 linked pull request 中看到的那样,你的扩展方法看起来像这样(注意,我在这里组合了两种方法以使其清楚):

public static async Task ShipAsync(this Exception exception, HttpContext context)
{
    await MessageShipper.ShipAsync(
        ElmahIoMiddleware.ApiKey.AssertApiKeyInMiddleware(),
        ElmahIoMiddleware.LogId.AssertLogIdInMiddleware(),
        exception.Message, context, new ElmahIoSettings(), exception);
}

为什么这很重要?因为你正在通过HttpContext。 HTTP 上下文使我们能够访问依赖注入。所以我们实际上可以从依赖注入容器中解决问题:

public static async Task ShipAsync(this Exception exception, HttpContext context)
{
    var elmahIoConfig = context.RequestServices.GetService<ElmahIoConfiguration>();

    await MessageShipper.ShipAsync(
        elmahIoConfig.ApiKey,
        elmahIoConfig.LogId,
        exception.Message, context, new ElmahIoSettings(), exception);
}

现在,您只需要确保在注册中间件时 ElmahIoConfiguration 被正确填充。请注意,我在这里没有请求中间件实例,因为中间件通常没有注册依赖注入。引入一个单独的对象更有意义,然后您可以将其注入中间件并在那里进行初始化。

在你的情况下,你真的应该将中间件配置移动到你的应用程序设置中,并使用 IOptions<> 模式在 ConfigureServices 级别配置它。在 Configure 中将中间件添加到管道时配置中间件在 ASP.NET Core 2 中不再是一种常见模式。