【问题标题】:Installing a new middleware at runtime in ASP.Net Core在 ASP.Net Core 运行时安装新的中间件
【发布时间】:2017-02-21 11:13:09
【问题描述】:

当我的应用程序启动时,我有一堆模块(模块 1、模块 2 ……)。对于每个模块,我都有一堆控制器操作:

/myModuleController/module1/action1
/myModuleController/module1/action2
/myModuleController/module2/action1
/myModuleController/module2/action2
…

由于用户可以在每个模块中登录一次,因此我为每个模块部署了一个身份验证中间件,这样做很简单:

app.UseWhen((context) => context.Request.Path.StartsWithSegments(urlPath), appbuilder =>
    {
        appbuilder.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            CookieName = cookieName,
            …
        });
    });

所以基本上,在 url 路径 /myModuleController/module1 上,我有一个中间件和它的 cookie,另一个用于 /myModuleController/module2 ......我想这有点不寻常,但它工作正常,我对这种行为感到满意。

问题来了:我希望能够在运行时添加一个新模块,这意味着能够使用类似app.UseWhen(url, app. UseCookieAuthentication(…)) 的一段代码部署一个新的中间件。我天真地尝试在负责添加模块的控制器中注入IApplicationBuilder app,但我得到了一个异常:

System.InvalidOperationException:尝试激活“AdminController”时无法解析“Microsoft.AspNetCore.Builder.IApplicationBuilder”类型的服务

我的问题是:它应该工作吗?我一定是在某个地方犯了错误?或者,你清楚我在这里尝试的东西没有机会工作吗?

您将如何达到相同的要求? 谢谢。

【问题讨论】:

  • 在启动代码运行后,您将无法添加新的中间件。所以你最好确定每个模块的 cookie 之间有什么不同,然后编写一个更通用的中间件来处理你的所有场景。

标签: c# authentication dependency-injection asp.net-core


【解决方案1】:

首先我们需要一个服务来保存运行时中间件的配置

public class RuntimeMiddlewareService
{
    private Func<RequestDelegate, RequestDelegate> _middleware;

    private IApplicationBuilder _appBuilder;

    internal void Use(IApplicationBuilder app)
    => _appBuilder = app.Use(next => context => _middleware == null ? next(context) : _middleware(next)(context));

    public void Configure(Action<IApplicationBuilder> action)
    {
        var app = _appBuilder.New();
        action(app);
        _middleware = next => app.Use(_ => next).Build();
    }
}

然后在Startup中添加一些扩展方法使用

public static class RuntimeMiddlewareExtensions
{
    public static IServiceCollection AddRuntimeMiddleware(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Singleton)
    {
        services.Add(new ServiceDescriptor(typeof(RuntimeMiddlewareService), typeof(RuntimeMiddlewareService), lifetime));
        return services;
    }

    public static IApplicationBuilder UseRuntimeMiddleware(this IApplicationBuilder app, Action<IApplicationBuilder> defaultAction = null)
    {
        var service = app.ApplicationServices.GetRequiredService<RuntimeMiddlewareService>();
        service.Use(app);
        if (defaultAction != null)
        {
            service.Configure(defaultAction);
        }
        return app;
    }
}

然后修改你的Startup

添加到ConfigureServices:

services.AddRuntimeMiddleware(/* ServiceLifetime.Scoped could be specified here if needed */);

添加到运行时指定的中间件应该在 Configure 内的位置。

app.UseRuntimeMiddleware(runtimeApp =>
{
    //runtimeApp.Use...
    //Configurations here could be replaced during the runtime.
});

最后,您可以从应用程序的其他部分重新配置运行时中间件

//var runtimeMiddlewareService = serviceProvider.GetRequiredService<RuntimeMiddlewareService>();
//Or resolved via constructor.
runtimeMiddlewareService.Configure(runtimeApp =>
{
    //runtimeApp.Use...
    //Configurations here would override the former ones.
});

【讨论】:

  • 看起来很有希望。那是你在生产中运行的东西吗?如果是这样,你能告诉我它使用的是哪个 .net 核心运行时吗?谢谢。
  • 我已经使用 .NET Core 3.0 preview6 对其进行了测试,并且运行良好。 @Daboul
  • 我会尽快尝试并回复评论
  • 很酷的东西!当我想为动态加载的资源(插件)添加UseStaticFiles 中间件时,这对我有很大帮助。 @Alsein - 尽管如果没有提供默认操作,可能会发生一个空引用错误。所以Use方法必须检查_middleware是否为空,如果是则返回next(context)
  • @MichelJansson 感谢您指出错误!我已经更新了答案。
猜你喜欢
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 2019-08-03
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多