net core 中间件管道

.net core 管道(Pipeline)是什么?

由上图可以看出,.net core 管道是请求抵达服务器到响应结果返回的中间的一系列的处理过程,如果我们简化一下成下图来看的话,.net core 的管道其实就是中间件的部分。微软中间件文档

为什么管道就是中间件的部分了呢?我是这么理解的,.net core 是通过Startup 类配置服务和应用的请求管道,所以狭义点来讲这个管道就是指的请求管道,就是我们今天要理解的中间件管道。

.net core 核心体系结构的特点就是一个中间件系统,它是处理请求和响应的代码段。中间件彼此链接,形成一个管道。传入的请求通过管道传递,其中每个中间件都有机会在将它们传递到下一个中间件之前对它们进行处理。传出响应也以相反的顺序通过管道传递。

PS:简单来讲就是请求开始到响应结束的中间的一大部分。你可以理解成 " 汽车销售 " (开始买车到提车的过程,但愿不会坐在奔驰车盖上哭),哈哈……

还有我们来看看,为什么我们要简化来看,在运行时 .net core 会预先注入一些必要的服务及依赖项,默认注入(ServiceCollection)的服务清单如下:

我们先断章取义地看,这里面有 Kestrel 处理请求,将接收到的请求内容(字符串流)转化成结构化的数据(HttpContext)供后面的中间件使用的服务。欸,服务哟。那其实也就是 Kestrel 服务也是中间件嘛。

而第一张图中的MVC本身也作为中间件来实现的。

还有一些相关的服务都可以看看上面截图的服务,而且旁边标注的生命周期的类型就是之前所讲到的 .net core 的三种注入模式 。

那么从程序入口来讲,过程是怎么样的呢?

从应用程序主入口 Main() --> WebHost --> UseStartup

/// <summary>
/// Specify the startup type to be used by the web host.
/// </summary>
/// <param name="hostBuilder">The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" /> to configure.</param>
/// <param name="startupType">The <see cref="T:System.Type" /> to be used.</param>
/// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
{
string name = startupType.GetTypeInfo().Assembly.GetName().Name;
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name).ConfigureServices((Action<IServiceCollection>)delegate(IServiceCollection services)
{
if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
{
ServiceCollectionServiceExtensions.AddSingleton(services, typeof(IStartup), startupType);
}
else
{
ServiceCollectionServiceExtensions.AddSingleton(services, typeof(IStartup), (Func<IServiceProvider, object>)delegate(IServiceProvider sp)
{
IHostingEnvironment requiredService = ServiceProviderServiceExtensions.GetRequiredService<IHostingEnvironment>(sp);
return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.get_EnvironmentName()));
});
}
});
}

上面的代码就可以解释说,会预先注入的必要的服务,在通过委托的方式,注入 Startup 里的服务。具体可以继续探究:

UseSetting:Add or replace a setting in the configuration.
/// <summary>
/// Add or replace a setting in the configuration.
/// </summary>
/// <param name="key">The key of the setting to add or replace.</param>
/// <param name="value">The value of the setting to add or replace.</param>
/// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
public IWebHostBuilder UseSetting(string key, string value)
{
_config.set_Item(key, value);
return this;
}
ConfigureServices:Adds a delegate for configuring additional services for the host or web application. This may be called multiple times.
/// <summary>
/// Adds a delegate for configuring additional services for the host or web application. This may be called
/// multiple times.
/// </summary>
/// <param name="configureServices">A delegate for configuring the <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection" />.</param>
/// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
{
if (configureServices == null)
{
throw new ArgumentNullException("configureServices");
}
return ConfigureServices(delegate(WebHostBuilderContext _, IServiceCollection services)
{
configureServices(services);
});
}

ConventionBasedStartup

public class ConventionBasedStartup : IStartup
{
private readonly StartupMethods _methods; public ConventionBasedStartup(StartupMethods methods)
{
_methods = methods;
} public void Configure(IApplicationBuilder app)
{
try
{
_methods.ConfigureDelegate(app);
}
catch (Exception ex)
{
if (ex is TargetInvocationException)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
throw;
}
} public IServiceProvider ConfigureServices(IServiceCollection services)
{
try
{
return _methods.ConfigureServicesDelegate(services);
}
catch (Exception ex)
{
if (ex is TargetInvocationException)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}
throw;
}
}
}

OK,到这里就已经确定了,我们可控的是通过Startup注入我们所需的服务,就是Startup注入的中间件可以做所有的事情,如处理认证,错误,静态文件等等,并且如上面所说的 MVC 在 .net core 也是作为中间件实现的。

那么 .net core 给我们内置了多少中间件呢?如下图:

我们很经常用到的内置中间件有:

    app.UseExceptionHandler(); //异常处理
app.UseStaticFiles(); //静态文件
app.UseAuthentication(); //Auth验证
app.UseMvc(); //MVC

我们知道可以在启动类的 Configure 方法中配置 .net core 管道,通过调用 IApplicationBuilder 上的 Use*** 方法,就可以向管道添加一个中间件,被添加的顺序决定了请求遍历它们的顺序。因此,如上面添加内置中间件的顺序,传入的请求将首先遍历异常处理程序中间件,然后是静态文件中间件,然后是身份验证中间件,最终将由MVC中间件处理。

Use*** 方法实际上只是 .net core 提供给我们的“快捷方式”,以便更容易地构建管道。在幕后,它们最终都使用(直接或间接)这些关键字:Use 和 Run 。两者都向管道中添加了一个中间件,不同之处在于Run添加了一个终端中间件,即管道中的最后一个中间件。

那么有内置,就应该可以定制的。如何定制自己的中间件呢?上一些简陋的demo演示一下:

(1)无分支管道

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ // Middleware A
app.Use(async (context, next) =>
{
Console.WriteLine("A (before)");
await next();
Console.WriteLine("A (after)");
}); // Middleware B
app.Use(async (context, next) =>
{
Console.WriteLine("B (before)");
await next();
Console.WriteLine("B (after)");
}); // Middleware C (terminal)
app.Run(async context =>
{
Console.WriteLine("C");
await context.Response.WriteAsync("Hello world");
}); }

打印结果:

A (before)
B (before)
C
B (after)
A (after)

那用管道图展示的话就是:

(2)有分支管道,当使用无分支的管道时,相当于就是一条线直走到底再返回响应结果。但一般情况下,我们都希望管道更具灵活性。创建有分支的管道就需要使用到 Map 扩展用作约定来创建管道分支。Map 是基于给定请求路径的匹配项来创建请求管道分支的,如果请求路径以给定的路径开头,就执行分支。那么就有两种类型有分支的管道:

1.无连结分支,上官方demo:

public class Startup
{
private static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
} private static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
} public void Configure(IApplicationBuilder app)
{
app.Map("/map1", HandleMapTest1); app.Map("/map2", HandleMapTest2); app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
}
}

结果:

以上无连结分支很容易就理解了,就是不同的路径跑不同的分支。如果是有参数匹配的话,就要使用 MapWhen,而 MapWhen 基于给定谓词的结果创建请求管道分支。Func<HttpContext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。 谓词用于检测查询字符串变量 branch 是否存在。

2.有连结(重新连接上主管道)分支,创建有连结分支管道就要使用到 UseWhen,上demo:

public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
Console.WriteLine("A (before)");
await next();
Console.WriteLine("A (after)");
}); app.UseWhen(
context => context.Request.Path.StartsWithSegments(new PathString("/foo")),
a => a.Use(async (context, next) =>
{
Console.WriteLine("B (before)");
await next();
Console.WriteLine("B (after)");
})); app.Run(async context =>
{
Console.WriteLine("C");
await context.Response.WriteAsync("Hello world");
});
}

像上面的代码,当请求不是以 " /foo " 开头的时候,结果为:

A (before)
C
A (after)

当请求是以 " /foo " 开头的时候,结果为:

A (before)
B (before)
C
B (after)
A (after)

正如您所看到的,中间件管道背后的思想非常简单,但是非常强大。大多数功能都是 .net core(身份验证、静态文件、缓存、MVC等)作为中间件实现。当然,编写自己的代码也很容易!

后面可以进阶写自己的中间件,官方文档

net core 中间件管道的更多相关文章

  1. .net core 中间件管道底层剖析

    .net core 管道(Pipeline)是什么? 由上图可以看出,.net core 管道是请求抵达服务器到响应结果返回的中间的一系列的处理过程,如果我们简化一下成下图来看的话,.net core ...

  2. .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法

    .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简单的话,一条主管道就够了,确实用不到 ...

  3. .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类

    .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类 0x00 为什么要引入扩展方法 有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件.除 ...

  4. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理

    .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...

  5. .Net Core:Middleware中间件管道

    .NetCore中的Middleware是装配到管道处理请求和响应的组件:每个组件都可以决定是否继续进入下一个管道.并且可以在进入下一个管道前后执行逻辑: 最后一个管道或者中断管道的中间件叫终端中间件 ...

  6. asp.net core mvc 管道之中间件

    asp.net core mvc 管道之中间件 http请求处理管道通过注册中间件来实现各种功能,松耦合并且很灵活 此文简单介绍asp.net core mvc中间件的注册以及运行过程 通过理解中间件 ...

  7. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  8. ASP.NET Core 中间件详解及项目实战

    前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World,如果你觉得本篇文章 ...

  9. [转]ASP.NET Core 中间件详解及项目实战

    本文转自:http://www.cnblogs.com/savorboard/p/5586229.html 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际 ...

随机推荐

  1. Go丨语言对MySQL数据库的增、删、改、查操作

    1.建立数据库名为: go_test_db 2.建表名为:userinfo 字段: uid int username varchar language varchar created varchar ...

  2. C/C++语法知识点汇总

    *  静态局部变量,在不同函数中可以同名. 静态全局变量,在不同文件中可以同名. 静态函数,在不同文件中可以同名. *  普通全局变量和普通函数,在同一工程中不能同名. 在相链接的程序与库之间,可以同 ...

  3. u3d 多线程 网络

    开启一个线程做网络连接,和接收数据, 用event进行广播 using UnityEngine; using System; using System.Threading; using System. ...

  4. noip模拟赛 #2

    万年rk2 我写挂大家都挂但是有人比我挂的少 我好不容易卡一波常数然后有人ak ... T1.不想写,等会放链接 T2 给一个方阵,每个地方有一个权值,把它划成两块,不能往回拐弯,求两块极差较大的那个 ...

  5. 休假回来 更博-MySQL以月为单位的客户综合情况表_20161008

    十一休假老家事比较多 未来得及更新 今起依旧更博- 生成一个以用户ID为单位,各月下单天次,各月买了几个产品,各月订单额 ,天次,,天次,,天次,NULL)) AS 9月天次 FROM ( SELEC ...

  6. bzoj 3376 [Usaco2004 Open]Cube Stacking 方块游戏——带偏移量的并查集

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3376 带偏移量的并查集. #include<iostream> #include ...

  7. BZOJ3165:[HEOI2013]Segment

    浅谈标记永久化:https://www.cnblogs.com/AKMer/p/10137227.html 题目传送门:https://www.lydsy.com/JudgeOnline/proble ...

  8. selenium+python中,框架中,怎么返回上一个菜单

    /退回上一级表单 driver.switchTo().defaultContent();

  9. 自动化测试【Maven+Eclipse+Selenium+Java环境搭建和测试】

    一.下载必要的文件 1.eclipse Eclipse官网 2.jdk jdk官网 3.selenium IDE.Selenium Server.Selenium Client Drivers(Jav ...

  10. iOS三方支付--微信支付/支付宝支付

    一.微信支付 1.注册账号并申请app支付功能 公司需要到微信开放品台进行申请app支付功能 , 获得appid和微信支付商户号(mch_id)和API秘钥(key) . Appsecret(secr ...