原文: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. Spring基础知识备案

    关于@Value注解不能为静态变量赋值的问题 // eg:(xxx.ooo.value=100) 以下这种方式,来自配置文件的属性值无法注入: public class XxxUtils { @Val ...

  2. java TripleDES加密

    package com.zhx.base.util; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher ...

  3. 2019工作计划idea

    2019.2.24 工作需求: 汇总 2008-2018年 销售订单数据; 分类历史订单数据, 并可能采取方法进行预测(预测只是一种行为不代表结果) 目前已知条件: 订单生产周期; 45天(标准天数) ...

  4. HTML 5将给开发者带来什么?

    在新的时代里,相信网页技术会伴随HTML 5的来临进入大洗牌的局面,HTML 5旨在解决Web中的交互,媒体,本地操作等问题,一些浏览器已经尝试支持HTML 5的一些功能,而开发者们有望最终从那些We ...

  5. js实现八皇后,回溯法

    八皇后问题:将八个皇后摆在一张8*8的国际象棋棋盘上,使每个皇后都无法吃掉别的皇后,一共有多少种摆法? 两个皇后不能同时在同一行,同一列,和斜对角线的位置上,使用回溯法解决. 从第一行选个位置开始放棋 ...

  6. 导航栏动态添加act属性

    最近做了一个网站,需要设置导航栏的act属性,这里需要用到addClass以及removeClass: $('#topName li').removeClass('active'); $(this). ...

  7. Mac_Sublime Text3(mac)一些插件和快捷键

    下载地址http://www.sublimetext.com/3 一.安装Package Control 按Ctrl + ` 调出console,粘贴下列安装代码到底部命令行并回车: import u ...

  8. (转)基因芯片数据GO和KEGG功能分析

    随着人类基因组计划(Human Genome Project)即全部核苷酸测序的即将完成,人类基因组研究的重心逐渐进入后基因组时代(Postgenome Era),向基因的功能及基因的多样性倾斜.通过 ...

  9. cadence布线约束规则设置

    DRC检查规则在布线过程中是必不可少的,包括时序规则,走线规则,间距规则,信号完整性规则和物理规则等,在绘制电路板时,设计相关规则满足设计需求,是非常关键的! https://wenku.baidu. ...

  10. python从入门到实践-6章字典

    #!/user/bin/env python# -*- coding:utf-8 -*- # 前面不用空格,后面空格# 访问只能通过keyalien_0 = {'color': 'green', 'p ...