zoukankan      html  css  js  c++  java
  • 探索ASP.NET Core中的IStartupFilter

    原文:Exploring IStartupFilter in ASP.NET Core
    作者:Andrew Lock
    译者:Lamond Lu

    在本篇博客中,我将介绍一下IStartupFilter, 以及如何在ASP.NET Core中使用它。在下一篇博客中,我将介绍一下如何在外部中间件中使用IStartupFilter

    IStartupFilter接口

    IStartupFilter接口存在于Microsoft.AspNetCore.Hosting.Abstractions程序集中,它非常简单,仅定义了一个接口方法。

    namespace Microsoft.AspNetCore.Hosting
    {
        public interface IStartupFilter
        {
            Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
        }
    }
    

    其中Configure方法返回了一个变量Action

    当创建一个ASP.NET Core应用程序的时候,IApplicationBuilder负责配置ASP.NET Core的中间件管道。例如你可以在Startup.cs文件的Configure方法中,看到以下类似的代码。

    public void Configure(IApplicationBuilder app)
    {
        app.UseStaticFiles();
    
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
    

    在这个方法中,你可以直接使用方法提供的IApplicationBuilder参数,并且可以向其中添加各种中间件。使用IStartupFilter, 你可以指定并返回一个Action类型的泛型委托,这意味你除了可以使用方法提供的泛型委托配置IApplicationBuilder对象, 还需要返回一个泛型委托。

    IStartupFilter方法可以接受一个配置IApplicationBuilder的方法,换而言之IStartupFilter.Configure方法可以使用Startup.Configure方法作为参数。

    例:

    Startup _startup = new Startup();
    Action<IApplicationBuilder> startupConfigure = _startup.Configure;
    
    //后续会补充StartupFilter1类的代码
    IStartupFilter filter1 = new StartupFilter1(); 
    
    Action<IApplicationBuilder> filter1Configure = filter1.Configure(startupConfigure)
    
    //后续会补充StartupFilter2类的代码
    IStartupFilter filter2 = new StartupFilter2(); 
    
    Action<IApplicationBuilder> filter2Configure = filter2.Configure(filter1Configure)
    

    如果之前你学习过ASP.NET Core的中间件管道,对于这个代码,你可能会感觉很熟悉。这里我们正在建立另一条管道, 它是一个Configure方法的管道,而不是中间件管道。 这就是IStartupFilter的目的,允许在应用程序中创建Configure方法的管道。

    实现IStartupFilter接口的对象何时会被调用?

    现在我们对IStartupFilter的签名有了更进一步的理解,接下来我们可以看看它在ASP.NET Core框架中的用法。

    要查看IStartupFilter是如果被调用的,你可以在查看Microsoft.AspNetCore.Hosting程序集中的WebHost类。 当你在WebHostBuilder对象上调用Build方法时,实现IStartupFilter接口对象会被调用。 这个代码通常出现在Program.cs文件中,例如:

    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()    
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .Build();  // 这个会调用BuildApplication方法
    
            host.Run(); 
        }
    }
    

    下面是BuildApplication方法的部分代码,你可以看到这个方法负责初始化中间件管道。方法的返回值RequestDelegate表示了一个完整的管道,当请求到达的时候,Kestral服务器可以调用它。

    private RequestDelegate BuildApplication()
    {
        ..
        IApplicationBuilder builder = builderFactory.CreateBuilder(Server.Features);
        builder.ApplicationServices = _applicationServices;
    
        var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
        Action<IApplicationBuilder> configure = _startup.Configure;
        foreach (var filter in startupFilters.Reverse())
        {
            configure = filter.Configure(configure);
        }
    
        configure(builder);
    
        return builder.Build();
    }
    

    首先,此方法创建IApplicationBuilder的实例,该实例将用于构建中间件管道,并将ApplicationServices设置为已配置的DI容器。

    接下来的代码块很意思。首先,从DI容器中获取了一个集合IEnumerable<IStartupFilter。正如我前面说的那样,我们可以配置多个IStartupFilter来形成一个管道,所以这个方法只是从容器中取出它们。此外,Startup.Configure方法被保存到局部变量configure中, 这就是通常在Startup类中编写的Configure方法,用于配置中间件管道。

    现在我们通过循环遍历每个IStartupFilter(以相反的顺序),传入Startup.Configure方法,然后更新局部变量configure来创建Configure方法的管道。这种方式实现了一种嵌套管道的效果。例如,如果我们有三个IStartupFilter实例,你最终会得到类似这样的东西,其中内部Configure方法在参数中传递给外部方法:

    局部变量configure的最终值会被IApplicationBuilder调用来执行实际的中间件管道配置。 调用builder.Build方法之后会生成处理HTTP请求所需的RequestDelegate

    一个IStartupFilter的例子

    前面我虽然描述了IStartupFilter的用途,但是可能查看一些现成的实现会更容易理解一些。 默认情况下,WebHostBuilder在初始化时会注册一个IStartupFilter - AutoRequestServicesStartupFilter

    public class AutoRequestServicesStartupFilter : IStartupFilter
    {
        public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
        {
            return builder =>
            {
                builder.UseMiddleware<RequestServicesContainerMiddleware>();
                next(builder);
            };
        }
    }
    

    本质上,它在中间件管道的开头添加了一个额外的中间件,即RequestServicesContainerMiddleware

    这是唯一一个默认注册的IStartupFilter,因此在这种情况下,参数next将是Startup类的Configure方法。

    这基本上就是IStartupFilter的全部内容 - 它是一种在配置的管道的开头或结尾添加额外中间件(或其他配置)的方法。

    如何注册IStartupFilter

    注册IStartupFilter很简单,只需像往常一样在你的ConfigureServices方法中注册它。 默认情况下,在WebHostBuilder中已经注册了AutoRequestServicesStartupFilter

    private IServiceCollection BuildHostingServices()
    {
        ...
        services.AddTransient<IStartupFilter, AutoRequestServicesStartupFilter>();
        ...
    }
    

    RequestServicesContainerMiddleware中间件

    以下是RequestServicesContainerMiddleware的部分代码

    public class RequestServicesContainerMiddleware
    {
        private readonly RequestDelegate _next;
        private IServiceScopeFactory _scopeFactory;
    
        public RequestServicesContainerMiddleware(RequestDelegate next, IServiceScopeFactory scopeFactory)
        {
            _scopeFactory = scopeFactory;
            _next = next;
        }
    
        public async Task Invoke(HttpContext httpContext)
        {
            var existingFeature = httpContext.Features.Get<IServiceProvidersFeature>();
    
            if (existingFeature?.RequestServices != null)
            {
                await _next.Invoke(httpContext);
                return;
            }
    
            using (var feature = new RequestServicesFeature(_scopeFactory))
            {
                try
                {
                    httpContext.Features.Set<IServiceProvidersFeature>(feature);
                    await _next.Invoke(httpContext);
                }
                finally
                {
                    httpContext.Features.Set(existingFeature);
                }
            }
        }
    }
    

    该中间件负责设置IServiceProvidersFeature。 创建时,RequestServicesFeature为请求创建新的IServiceScopeIServiceProvider。 它将负责使用Scoped生命周期添加到DI容器的依赖项的创建和处理。

    IStartupFilter的使用场景

    一般来说,我不认为在用户的应用程序中需要使用IStartupFilter。 就其本质而言,用户可以在Configure方法中定义中间件管道,因此IStartupFilter是不必要的。

    我能想到以下几种需要使用IStartupFilter的场景:

    • 你自己创建了一个库,你需要确保你的中间件在中间件管道的开头(或结尾)运行。
    • 你正在使用一个使用IStartupFilter的库,您需要确保您的中间件在它之前运行。

    总结

    本篇博文中,我讲解了IStartupFilter以及WebHost如何使用它在构建中间件管道。 在下一篇文章中,我将探讨IStartupFilter的具体用法。

    后记

    本篇是作者早期的一篇博文,个人觉着对IStartupFilter讲解的比较清楚,就翻译了一下。在作者的后期博文中,作者提供了许多IStartupFilter的使用场景,例如

    有兴趣的同学可以自己阅读一下,后续我会选择一些有意思的文章翻译一下。

  • 相关阅读:
    HDFS镜像文件的解析与反解析
    HDFS镜像文件的解析与反解析
    Nginx将通过IP访问重定向
    Nginx反向代理导致PHP获取不到正确的HTTP_HOST,SERVER_NAME,客户端IP的解决方法
    PHP个人常用函数封装
    apache禁止公网IP访问的配置
    Linux查看端口被哪个进程占用
    Nginx负载均衡配置
    Nginx反向代理配置文件
    Apache多端口配置
  • 原文地址:https://www.cnblogs.com/lwqlun/p/10279874.html
Copyright © 2011-2022 走看看