今天来讨论一个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. input输入框中只能输入数字,非数字字符自动清除

    前言:项目中有个缴纳保证金的功能,要是输入框只能输入数字,不能输入其他字符. ①HTML代码:<input class="input-box" type="text ...

  2. jar包读取配置文件

    读取jar包内配置文件: Properties config = new Properties(); InputStream in = this.getClass().getClassLoader() ...

  3. 【c++】访问控制

    1.  类内的访问控制 在基类中,public和private具有普通的含义:用户(即基类的对象)可以访问public成员(包括函数.数据),而不能访问private成员.private只能被基类的成 ...

  4. 点击checkbox全选,其它被选中,再点击取消

    <input type="checkbox" value="" id="checkall" name="" siz ...

  5. Ubuntu17.10下编译Openjdk8u

    一开始笔者用的系统和软件版本都是最新的,导致编译了好几次都失败,最后找到解决的办法,现在记录一下编译及解决的方法 避免以后忘记 所用操作系统 Ubuntu17.10 所用软件及版本 make 3.8. ...

  6. centos下如何停止ping命令

    ctrl + c 或者 Ctrl + d(好像不行) man ping

  7. Navicat 连接MySQL 8.0.11 出现2059错误

    错误 使用Navicat Premium 连接MySQL时出现如下错误: 原因 mysql8 之前的版本中加密规则是mysql_native_password,而在mysql8之后,加密规则是cach ...

  8. Java finally关键字

    关于finally语句块,有如下特点: 1.finally语句块可以直接和try语句块联用.try...finally... 2.try...catch...finally也可以 3.通常在final ...

  9. 11.6NOIP模拟赛解题报告

    心路历程 预计得分:\(100 + 100 + 100 = 300\) 实际得分:\(100 +100 +100 = 300\) 学OI两年终于AK了一次qwq(虽然题目炒鸡水..) 纪念一下这令人激 ...

  10. HADOOP背景介绍

    1. HADOOP背景介绍 1.1 什么是HADOOP 1. HADOOP是apache旗下的一套开源软件平台 2. HADOOP提供的功能:利用服务器集群,根据用户的自定义业务逻辑,对海量数据进行分 ...