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

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

探索ASP.NET Core中的IStartupFilter的更多相关文章

  1. 探索ASP.Net Core 3.0系列四:在ASP.NET Core 3.0的应用中启动时运行异步任务

    前言:在本文中,我将介绍ASP.NET Core 3.0 WebHost的微小更改如何使使用IHostedService在应用程序启动时更轻松地运行异步任务. 翻译 :Andrew Lock   ht ...

  2. 探索 ASP.Net Core 3.0系列三:ASP.Net Core 3.0中的Service provider validation

    前言:在本文中,我将描述ASP.NET Core 3.0中新的“validate on build”功能. 这可以用来检测您的DI service provider是否配置错误. 具体而言,该功能可检 ...

  3. 探索ASP.Net Core 3.0系列六:ASP.NET Core 3.0新特性启动信息中的结构化日志

    前言:在本文中,我将聊聊在ASP.NET Core 3.0中细小的变化——启动时记录消息的方式进行小的更改. 现在,ASP.NET Core不再将消息直接记录到控制台,而是正确使用了logging 基 ...

  4. 探索ASP.Net Core 3.0系列二:聊聊ASP.Net Core 3.0 中的Startup.cs

    原文:探索ASP.Net Core 3.0系列二:聊聊ASP.Net Core 3.0 中的Startup.cs 前言:.NET Core 3.0 SDK包含比以前版本更多的现成模板. 在本文中,我将 ...

  5. ASP.NET Core中使用GraphQL - 第四章 GraphiQL

    ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 ASP ...

  6. Asp.net core中的依赖注入

    使用服务 在Asp.net core的Controller中,可以通过如下两种方式获取系统注入的服务: 构造函数 可以直接在构造函数中传入所依赖的服务,这是非常常见的DI注入方式. public Va ...

  7. 探索Asp net core3中的 项目文件、Program.cs和通用host(译)

    引言 原文地址 在这篇博客中我将探索一些关于Asp.net core 3.0应用的基础功能--.csproj 项目文件和Program源文件.我将会描述他们从asp.net core 2.X在默认模版 ...

  8. 探索ASP.NET Core 3.0系列一:新的项目文件、Program.cs和generic host

    前言:在这篇文章中我们来看看ASP.Net Core 3.0应用程序中一些基本的部分—— .csproj项目文件和Program.cs文件.我将会介绍它们从 ASP.NET Core 2.x 中的默认 ...

  9. [转]探索ASP.NET Core 3.0 系列

    这是该系列的第一篇文章:探索ASP.NET Core 3.0. 第1部分-探索新的项目文件Program.cs和通用主机(本文) 第2部分-比较ASP.NET Core 3.0模板之间的Startup ...

随机推荐

  1. Maven导入项目时报错 Could not calculate build plan

    Could not calculate build plan: Plugin org.apache.maven.plugins:maven-war-plugin:2.2 or one of its d ...

  2. 动态规划——Dungeon Game

    这又是个题干很搞笑的题目:恶魔把公主囚禁在魔宫的右下角,骑士从魔宫的左上角开始穿越整个魔宫到右下角拯救公主,为了以最快速度拯救公主,骑士每次只能向下或者向右移动一个房间, 每个房间内都有一个整数值,负 ...

  3. Windows本地代码仓库使用连接教程

    目录 软件安装 修改语言为中文 克隆远程仓库 文件上传教程 软件安装 安装Git(软件下载链接) 根据自己的系统选择对应版本下载安装 安装TortoiseGit(软件下载链接) 1.下载完毕解压文件夹 ...

  4. That girl

    音标 词汇 Purple Glasses Black Hat Brown Bag Clothes Blue Jacket Pink Handbag Sock White Skirt Shoe 1, s ...

  5. Pi 3B+编译安装python3.6.8

    树莓派镜像版本2018-11-13,更新到2019-01-09 sudo apt-get update sudo apt-get upgrade -dev libgdbm-dev libsqlite3 ...

  6. 大数据平台Hive数据迁移至阿里云ODPS平台流程与问题记录

    一.背景介绍 最近几天,接到公司的一个将当前大数据平台数据全部迁移到阿里云ODPS平台上的任务.而申请的这个ODPS平台是属于政务内网的,因考虑到安全问题当前的大数据平台与阿里云ODPS的网络是不通的 ...

  7. 编程菜鸟的日记-Linux无处不在

    原文来自:http://www.linuxfederation.com/linux-everywhere Linux无处不在 “Linux无处不在.从空间站到微波炉到有Linux.”你可能听说很多以及 ...

  8. javaEE学习路线与目标

    1.Java基础知识(15-30天) 2.了解html+css+js+jq+bootstrap(7天) 3.mysql+jdbc(重点)(3天) 4.xml(1天) 5.http协议+tomcat(1 ...

  9. sv时序组合和时序逻辑

    input a; input b; input c; reg d; wire e; reg f; // 时序逻辑,有寄存器 always@(posedge clk)begin 'b1)begin d ...

  10. CentOS6 安装 MySQL5.7

    CentOS 6.10 编译安装 Mysql 5.7.23 X64 1.添加用户组和用户 1) 添加用户组和用户 groupadd mysql 2) 添加用户 useradd -g mysql -s ...