您的位置:首页 > 编程语言 > ASP

探索ASP.NET Core中的IStartupFilter

2019-01-16 22:41 1176 查看

原文: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
为请求创建新的
IServiceScope
IServiceProvider
。 它将负责使用Scoped生命周期添加到DI容器的依赖项的创建和处理。

IStartupFilter
的使用场景

一般来说,我不认为在用户的应用程序中需要使用

IStartupFilter
。 就其本质而言,用户可以在
Configure
方法中定义中间件管道,因此
IStartupFilter
是不必要的。

我能想到以下几种需要使用

IStartupFilter
的场景:

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

总结

本篇博文中,我讲解了

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

后记

本篇是作者早期的一篇博文,个人觉着对

IStartupFilter
讲解的比较清楚,就翻译了一下。在作者的后期博文中,作者提供了许多
IStartupFilter
的使用场景,例如

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: