今天来讨论一个ASP.NET Core 很重要概念管道和中间件,在ASP.NET Core中,针对HTTP请求采用pipeline也就是通常说的管道方式来处理,而管道容器内可以挂载很多中间件(处理逻辑)“串联”来处理HTTP请求,每一个中间件都有权决定是否需要执行下一个中间件,或者直接做出响应。这样的机制使得HTTP请求能够很好的被层层处理和控制,并且层次清晰处理起来甚是方便。 示意图如下:

  

  为了再次说明管道和中间件的概念,举一个官方给出的权限验证的例子,中间件A,B分别按顺序挂载在管道容器中,A为权限验证中间件,只有通过A的权限验证才能执行B,如果没有通过A的验证,A有权中断管道处理直接返回相应的错误提示例如401等。这样必须由上一节点来调用的串行递归执行方式就是pipeline,而每一个节点就是中间件或者叫中间组件。现在我们来看看如何在ASP.NET Core中使用中间件和管理自己的HTTP管道


  环境配置与Startup

  在了解中间件之前我们需要先知道Startup这个类具体运作方式,我们以下面这段代码为例:

    /// <summary>
/// web宿主的入口类
/// </summary>
public class Startup
{
//加入服务项到容器, 这个方法将会被runtime调用
public void ConfigureServices(IServiceCollection services)
{ } /// <summary>
/// 配置HTTP请求管道
/// </summary>
/// <param name="app">被用于构建应用程序的请求管道 只可以在Startup中的Configure方法里使用</param>
/// <param name="env">提供了访问应用程序属性,如环境变量</param>
/// <param name="loggerFactory">提供了创建日志的机制</param>
public void Configure(IApplicationBuilder app,IHostingEnvironment env,ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(); if (env.IsDevelopment()) //根据配置的环境为开发环境,则会配置抛出异常错误界面
{
app.UseDeveloperExceptionPage(); //抛出详细的异常错误界面
} //管道断路
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}

  可以看到 Startup.cs 内有两个方法,一个是用来配置接口服务到管道容器中的ConfigureServices, 一个是用来配置管道中间件的Configure。

  为什么必须是这两个方法名?

  其实这两个方法名并不是规定死的,但也不是任意规定的,他是根据容器的环境变量来判断的,这里先给出官方文档《多环境下工作》

  我们可以在文档中了解到,Core使用“ASPNETCORE_ENVIRONMENT”字段来描述当前运行环境名称这就是上文中提到的环境配置,官方预设了3个环境名分别是Development(开发环境), Staging(测试环境), Production(生产环境),如果您使用的是VSCode您可以在.vscode文件夹下的launch.json中找到“ASPNETCORE_ENVIRONMENT”字段,可以发现默认情况下是Development,那说这些到底有什么用呢?

  

  在Startup中规定,配置服务和中间件两个方法可以根据环境名称来命名和选择调用,命名规则为ConfigureServices{ENVIRONMENT}和Configure{ENVIRONMENT}。如 ASPNETCORE_ENVIRONMENT = “Development” 则ConfigureServices和Configure 可以写成ConfigureServicesDevelopment 和 ConfigureDevelopment ,其他也是如此。这样就可以通过配置ASPNETCORE_ENVIRONMENT 来决定该调用哪一个配置方法了。

  ConfigureServices和Configure是什么环境下的呢?

    ConfigureServices和Configure就好像Switch 语句中的 default一样的道理,如果没有找到任何符合环境名的方法名,就会执行调用这两个方法。如配置了Development,但却没有给出ConfigureServicesDevelopment ,这时就会执行ConfigureServices,如果都没有就会抛出异常。

  必须设置成预设环境名吗?

  环境名配置的参数名不必是预设值,你可以自己写一个,比如LogEnv等等。

  接下来我们看一下实现的代码:

     /// <summary>
/// web宿主的入口类
/// </summary>
public class Startup
{
//加入服务项到容器, 这个方法将会被runtime调用
public void ConfigureServices(IServiceCollection services)
{ } /// <summary>
/// Log环境下配置HTTP请求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureLogHelp(IApplicationBuilder app){
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World - ConfigureLogHelp");
});
} /// <summary>
/// 开发环境下配置HTTP请求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World - ConfigureDevelopment");
});
} /// <summary>
/// 默认情况下配置HTTP请求管道
/// </summary>
/// <param name="app">被用于构建应用程序的请求管道。只可以在 Startup 中的 Configure 方法里使用</param>
/// <param name="env">提供了访问应用程序属性,如环境变量</param>
/// <param name="loggerFactory">提供了创建日志的机制</param>
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ //管道断路
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}

Startup.cs

  当ASPNETCORE_ENVIRONMENT = “Development

  

  当ASPNETCORE_ENVIRONMENT = “LogHelp”

  

  这样做的好处就是你可以写自己的测试配置而不会影响到其他人或者开发过程。当然环境的作用还在于前端应该引用什么样的CSS和JS,关于这些我们之后在MVC的章节再来讨论, 想了解的博友可以看官方文档


  管道配置与Startup

  说完环境配置和Startup的关系,我们回来接着聊管道的事情,现在我们来说说Configure{ENVIRONMENT}一下Configure简称这个方法。

  Configure这个方法是用于配置中间件到中管道容器(IApplicationBuilder),所以这个方法必须要包含一个IApplicationBuilder参数用来接受管道容器,方便开发者配置。当然他还可以接受其他的可选参数供开发者使用如下:

  (注:下图来源于ASP.NET Core中文文档

  

  需要提一下的是,刚刚我们上文中说的环境名在IHostingEnvironment中可以获取,对于预设值官方还做了判断封装,当然你可以重构它来封装自己的环境名判断。

  HTTP管道容器由三个扩展的方法来控制中间件的路由、挂载等等,分别是Run, Map, User

  a. Run方法会使得可以使管道短路,顾名思义就是终结管道向下执行不会调用next()委托,所以Run方法最好放在管道的最后来执行,如下面的代码:

        /// <summary>
/// 开发环境下配置HTTP请求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World - ConfigureDevelopment");
}); app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World - ConfigureDevelopment 不会被执行");
}); }

  执行结果:

  

  b. Use不会主动短路整个HTTP管道,但是也不会主动调用下一个中间件,必须自行调用await next.Invoke(); 如果不使用这个方法去调用下一个中间件那么Use此时的效果其实和Run是相同的,我们来看正常的代码:

        /// <summary>
/// 开发环境下配置HTTP请求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
var order ="";
app.Use(async (context, next) =>
{
order = $"{order}|Use start";
await next.Invoke();
order = $"{order}|Use end";
}); app.Run(async context =>
{
await context.Response.WriteAsync($"{order}|Run ext");
}); }

  执行结果如下:

  

  可以看到,Use end并没有被执行到,因为在调用下一个中间件时采用了Run,管道被终止了。

  再来看看如果不显式调用next.Invoke()时的代码:

        /// <summary>
/// 开发环境下配置HTTP请求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
var order ="";
app.Use(async (context, next) =>
{
order = $"{order}|Use start";
//去掉显示调用下一个中间件
//await next.Invoke();
order = $"{order}|Use end";
await context.Response.WriteAsync(order);
}); app.Run(async context =>
{
await context.Response.WriteAsync($"{order}|Run ext");
}); }

  其结果如下:

  

  可以发现Run这个中间件并没有被执行,而只是单纯的执行了Use这个中间件。所以说 在不显式调用下一个中间件的情况下,效果和Run时一样的会使管道短路。

  c. Map可以根据提供的URL来路由中间件,如下代码判断URL中访问"/test"时就会执行某个中间件逻辑:

        /// <summary>
/// 开发环境下配置HTTP请求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
app.Map("/test", HandleMapTest);
} /// <summary>
/// maptest 处理方法
/// </summary>
public void HandleMapTest(IApplicationBuilder app){
app.Run(async (context) =>
{
await context.Response.WriteAsync("HandleMapTest Handler");
}); }

  结果如下:

  

  如果访问/test就会执行相应的中间件,反之则不会执行。

  MapWhen是Map的一个条件判断的扩展方法,可以通过它来判断某个条件适合的时候执行某一个中间件,如:当携带某一个参数名称时,执行某一个中间件或者反之,代码如下:

        /// <summary>
/// 开发环境下配置HTTP请求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
app.MapWhen(context => {
return context.Request.Query.ContainsKey("username");
}, HandleUserName);
app.Run(async context =>
{
await context.Response.WriteAsync("default ext");
});
} /// <summary>
///
/// </summary>
public void HandleUserName(IApplicationBuilder app){
app.Run(async context =>
{
await context.Response.WriteAsync("UserName Map");
});
}

  结果如下:

  

  

  Map还可以进行嵌套路由中间件,这里不再描述,大家可以参看这里

  今天的管道介绍就写到这里,希望能对大家有帮助,如有不对望多加指正。

  

  

  

ASP.NET Core - 中间件与管道(1)的更多相关文章

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

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

  2. 如何一秒钟从头构建一个 ASP.NET Core 中间件

    前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...

  3. ASP.NET Core中间件实现分布式 Session

    1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件的配置 1.2. 依赖注入中间件 1.3. Cookies ...

  4. ASP.NET Core 入门教程 9、ASP.NET Core 中间件(Middleware)入门

    一.前言 1.本教程主要内容 ASP.NET Core 中间件介绍 通过自定义 ASP.NET Core 中间件实现请求验签 2.本教程环境信息 软件/环境 说明 操作系统 Windows 10 SD ...

  5. ASP.NETCore学习记录(二) —— ASP.NET Core 中间件

    ASP.NET Core 中间件 目录: 什么是中间件 ? IApplicationBuilder 使用 IApplicationBuilder 创建中间件 Run.Map 与 Use 方法 实战中间 ...

  6. ASP.NET Core 中间件基本用法

    ASP.NET Core 中间件 ASP.NET Core的处理流程是一个管道,而中间件是装配到管道中的用于处理请求和响应的组件.中间件按照装配的先后顺序执行,并决定是否进入下一个组件.中间件管道的处 ...

  7. (4)ASP.NET Core 中间件

    1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以 ...

  8. ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 中间件 上一章节中,我们我们有讲到 Startup 类中的 Confi ...

  9. ASP.NET Core 中的管道机制

    首先,很感谢在上篇文章 C# 管道式编程 中给我有小额捐助和点赞的朋友们,感谢你们的支持与肯定.希望我的每一次分享都能让彼此获得一些收获,当然如果我有些地方叙述的不正确或不当,还请不客气的指出.好了, ...

随机推荐

  1. 如何制作高水平简历?&& 制作简历时需要注意的问题

    1. 投递简历时一定要署名.  无论是简历的名称还是投递到邮箱时的名称,都需要说明重要信息,即 姓名-职位-学校-专业 . 这样,hr在筛选.录入简历时可以很方便查找简历,这样也可以给hr.面试官一个 ...

  2. springboot整合mybatis(映射文件方式和注解方式)

    springboot作为一个微服务框架,给我们开发人员提供极大的便利,秉着约定大于配置的原则,通过starter包的形式为我们做了许多默认的配置,在进行数据持久化到关系型数据库时,我们一般都会首选sp ...

  3. Python生成pyc文件

    Python生成pyc文件 pyc文件是py文件编译后生成的字节码文件(byte code).pyc文件经过python解释器最终会生成机器码运行.所以pyc文件是可以跨平台部署的,类似Java的.c ...

  4. php过滤数组空值

    如果我们想过滤数组里面的空值,例如null,,false,' '等等,可以使用php自带的一个函数,使用起来非常方便简洁: //测试数据 $data = array( '0' => '测试内容1 ...

  5. SpringBoot | 第三十六章:集成多CacheManager

    前言 今天有网友咨询了一个问题:如何在一个工程中使用多种缓存进行差异化缓存,即实现多个cacheManager灵活切换.原来没有遇见这种场景,今天下班抽空试了下,以下就把如何实现的简单记录下. 一点知 ...

  6. SQLite.dll在xp中部署时的报错处理

    错误信息: System.IO.FileNotFoundException: Could not load file or assembly 'System.Data.SQLite.dll' or o ...

  7. 【JSON.parse()和JSON.stringify()】

    var str = '{"name":"huangxiaojian","age":"23"}' 结果: JSON.par ...

  8. 使用Having子句

    Having 子句与where子句的功能类似,都是对行进行筛选.但是,where搜索条件是在分组操作之前对记录进行筛选,然后再由group BY 对筛选后符合条件的行进行分组:而Having搜索条件则 ...

  9. java 并发(五)---AbstractQueuedSynchronizer(5)

    问题 : ArrayBlockQueue 和 LinkedBlockQueue 的区别 两者的实现又是怎么样的 应用场景 BlockingQueue 概述 blockingQueue 是个接口,从名字 ...

  10. xcode8 打开的 xib 踩坑

    之前开发都不敢工测试版的开发,一直用正式版的,xcode7.3.1的模糊匹配让我很蛋疼,自定义的类,类名不提示,每次都粘贴复制,8号苹果发布了 xcode8GM 版,迫不及待的从苹果开发者官网下了一个 ...