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的使用场景,例如

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

  • 相关阅读:
    Linux下sed,awk,grep,cut,find学习笔记
    Python文件处理(1)
    KMP详解
    Java引用详解
    解决安卓中页脚被输入法顶起的问题
    解决swfupload上传控件文件名中文乱码问题 三种方法 flash及最新版本11.8.800.168
    null id in entry (don't flush the Session after an exception occurs)
    HQL中的Like查询需要注意的地方
    spring mvc controller间跳转 重定向 传参
    node to traverse cannot be null!
  • 原文地址:https://www.cnblogs.com/lwqlun/p/10279874.html
Copyright © 2011-2022 走看看