ASP.NET Core的中间件与管道介绍

今天来讨论一个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!");
            });
        }

当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中间件与管道介绍的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • ASP.NET Core中间件初始化的实现

    前言 在日常使用ASP.NET Core开发的过程中我们多多少少会设计到使用中间件的场景,ASP.NET Core默认也为我们内置了许多的中间件,甚至有时候我们需要自定义中间件来帮我们处理一些请求管道过程中的处理.接下来,我们将围绕着以下几个问题来简单探究一下,关于ASP.NET Core中间件是如何初始化的 首先,使用UseMiddleware注册自定义中间件和直接Use的方式有何不同 其次,使用基于约定的方式定义中间件和使用实现IMiddleware接口的方式定义中间件有何不同 再次,使用基

  • .net core异常中间件的使用

    目录 正文 正文 if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } 这样写入中间件哈,那么在env环境下就会去执行UseDeveloperExceptionPage. public static IApplicationBuilder UseDeveloperExceptionPage(this IApplicationBuilder app) { if (app == null) { throw new Argument

  • 理解ASP.NET Core 中间件(Middleware)

    目录 中间件 中间件管道 Run Use UseWhen Map MapWhen Run & Use & UseWhen & Map & Map 编写中间件并激活 基于约定的中间件 基于工厂的中间件 基于约定的中间件 VS 基于工厂的中间件 中间件 先借用微软官方文档的一张图: 可以看到,中间件实际上是一种配置在HTTP请求管道中,用来处理请求和响应的组件.它可以: 决定是否将请求传递到管道中的下一个中间件 可以在管道中的下一个中间件处理之前和之后进行操作 此外,中间件的注

  • ASP.NET Core自定义中间件如何读取Request.Body与Response.Body的内容详解

    背景# 最近在徒手造轮子,编写一个ASP.NET Core的日志监控器,其中用到了自定义中间件读取Request.Body和Response.Body的内容,但是编写过程,并不像想象中的一帆风顺,ASP.NET Core针对Request.Body和Response.Body的几个特殊设计,导致了完成以上功能需要绕一些弯路. 原始代码# 为了读取Request.Body和Response.Body的内容,我的实现思路如下: 创建一个LoggerMiddleware的中间件,将它放置在项目中间件管

  • 详解ASP.NET Core中间件Middleware

    本文为官方文档译文,官方文档现已非机器翻译 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1 什么是中间件(Middleware)? 中间件是组装到应用程序管道中以处理请求和响应的软件. 每个组件: 选择是否将请求传递给管道中的下一个组件. 可以在调用管道中的下一个组件之前和之后执行工作. 请求委托(Request delegates)用于构建请求管道,处理每个HTT

  • ASP.NET Core的中间件与管道介绍

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

  • ASP.NET Core 中间件的使用之全局异常处理机制

    目录 1.创建项目 2.创建全局异常过滤器 3.依赖注入全局异常处理机制 4.测试全局异常处理机制 前言: 我们经常听到"秒修复秒上线",觉得很厉害的样子. 其实不然,这只是一个调侃而已,出现问题的方式很多(逻辑漏洞.代码异常.操作方式不正确等). 我们今天来说代码异常问题怎么快速定位,减少不必要的时间浪费. 这就是今天的主题"添加全局异常处理机制"捕捉异常存储到数据库(mongodb.SqlServer.MySQL等). PS:输出txt的话不怎么友好,不是所有人

  • .NET 6中间件Http Logging使用介绍

    Intro .NET 6 会引入一个 Http logging 的中间件,可以用来帮助我们比较方便记录请求和响应的信息 Sample 废话不多说,直接来看示例吧 var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var app = builder.Build(); app.UseHttpLogging(); app.MapControllers(); app.Run(); do

  • 在 asp.net core 的中间件中返回具体的页面的实现方法

    前言 在 asp.net core 中,存在着中间件这一概念,在中间件中,我们可以比过滤器更早的介入到 http 请求管道,从而实现对每一次的 http 请求.响应做切面处理,从而实现一些特殊的功能 在使用中间件时,我们经常实现的是鉴权.请求日志记录.全局异常处理等等这种非业务性的需求,而如果你有在 asp.net core 中使用过 swashbuckle(swagger).health check.mini profiler 等等这样的组件的话,你会发现,这些第三方的组件往往都提供了页面,允

  • ASP.NET Core基础之请求处理管道

    了解ASP.NET处理管道 为了理解ASP.NET Core中的请求处理管道概念,让我们修改Startup类的Configure()方法,如下所示. 在这里,我们将三个中间件组件注册到请求处理管道中. 如您所见,前两个组件是使用Use() 扩展方法注册的,因此它们有机会在请求处理管道中调用下一个中间件组件. 最后一个使用Run() 扩展方法注册,因为它将成为我们的终止组件,即它将不会调用下一个组件. 了解ASP.NET Core请求处理管道执行顺序 为了理解这一点,让我们将上面的输出与下图进行比

  • ASP.NET Core自定义中间件的方式详解

    目录 1.委托形式 2.强类型中间件 2.1.定义中间件的依赖 2.2.定义中间件类型 3.基于约定的中间件 3.1.约定规则 3.2.应用实现 总结 ASP.NET Core应用本质上,其实就是由若干个中间件构建成的请求处理管道.管道相当于一个故事的框架,而中间件就相当于故事中的某些情节.同一个故事框架采用不同的情节拼凑,最终会体现出不同风格的故事.而我们的ASP.NET Core应用也正是如此,同一管道采用不同的中间件组合,最终也会呈现出不同的应用形态. 从上述的概念种可以看出,中间件在AS

  • ASP.NET Core中的Razor页面介绍

    目录 简介 Why? 创建Razor页面应用程序 ASP.NETCoreRazor页面-核心功能 模型绑定 Handlers TagHelpersandHTMLHelpers 路由 总结 简介 随着ASP.NET Core 2 即将来临,最热门的新事物是Razor页面. Razor页面是ASP.NET Core的一个新功能,可以使基于页面的编程方式更容易,更高效. 大众的初步印象是对于那些只专注于页面的小型应用来说,Razor页面更容易.更快地取代MVC.然而,事实证明,它可能比这更强大.使用A

  • ASP.NET Core中的静态文件介绍

    静态文件(HTML,CSS,图片和Javascript之类的资源)会被ASP.NET Core应用直接提供给客户端. 静态文件通常位于网站根目录(web root) <content-root>/wwwroot文件夹下.通常会把项目的当前目录设置为Content root,这样项目的web root就可以在开发阶段被明确. public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.Create

  • ASP.NET Core实现中间件的几种方式

    前言 ASP.NET Core 中 HTTP 管道使用中间件组合处理的方式, 换句人话来说, 对于写代码的人而言,一切皆中间件. 业务逻辑/数据访问/等等一切都需要以中间件的方式来呈现. 那么我们必须学会如何实现自定义中间件 这里划重点,必考 这里我们介绍下中间件的几种实现方式... 匿名函数 通常新建一个空的 ASP.NET Core Web Application,项目名字无所谓啦 在启动类里可以看到这么一句: // Startup.cs // ... app.Run(async (cont

  • Asp.Net Core 通过中间件防止图片盗链的实例

    一.原理 要实现防盗链,我们就必须先理解盗链的实现原理,提到防盗链的实现原理就不得不从HTTP协议说起,在HTTP协议中,有一个表头字段叫referer,采用URL的格式来表示从哪儿链接到当前的网页或文件.换句话说,通过referer,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以跟踪到显示它的网页地址.有了referer跟踪来源就好办了,这时就可以通过技术手段来进行处理,一旦检测到来源不是本站即进行阻止或者返回指定的页面.如果想对自己的网站进行防盗链保护,则需要针对不同的情况进行区

  • ASP.NET Core中的对象池介绍

    asp.net core中通过扩展库的方式提供给了一个标准的对象池ObjectPool,定义在Microsoft.Extensions.ObjectPool.dll 程序集中.它本身是个纯虚的抽象类,它就定义了两个接口函数,实现如下 public abstract class ObjectPool<T> where T : class { public abstract T Get(); public abstract void Return(T obj); } 这是一个比较典型的对象池接口:

随机推荐