ASP.NET Core - 中间件与管道(1)

  今天来讨论一个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还可以进行嵌套路由中间件,这里不再描述,大家可以参看这里

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

  

  

  

时间: 2024-08-11 05:45:03

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

Asp.net core 中间件简单应用

Asp.net core中间件 ,处理http请求和响应的中间组件,对比起asp.net ,asp.net core 管道机制,可以说是帅气十足,简单直接.下面是通过中间件对一个请求的url 指定路由 新建webapi 项目 Startup类中Configure方法中添加处理中间件代码如下 public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app

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

ASP.NET Core 中间件 目录: IApplicationBuilder 什么是中间件 ? 使用 IApplicationBuilder 创建中间件 Run.Map 与 Use 方法 实战中间件 参考原文 我们知道在 ASP.NET 中,有一个面向切面的请求管道,由22个主要的事件构成,能够让我们在往预定的执行顺序里面添加自己的处理逻辑.一般采取两种方式:一种是直接在 Global.asax 中对应的方法中直接添加代码.一种是是在 web.config 中通过注册 HttpModule

ASP.NET Core 中间件基本用法

ASP.NET Core 中间件 ASP.NET Core的处理流程是一个管道,而中间件是装配到管道中的用于处理请求和响应的组件.中间件按照装配的先后顺序执行,并决定是否进入下一个组件.中间件管道的处理流程如下图(图片来源于官网): 管道式的处理方式,更加方便我们对程序进行扩展. 使用中间件 ASP.NET Core中间件模型是我们能够快捷的开发自己的中间件,完成对应用的扩展,我们先从一个简单的例子了解一下中间件的开发. Run 首先,我们创建一个ASP.NET Core 应用,在Startup

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

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

12.ASP.NET Core 中间件组件

这篇文章中,我将带领大家一起详细学习:ASP.NET Core Middleware Components.这篇文章中,我将详细讨论下面几个问题: 什么是ASP.NET Core 中的中间件组件? ASP.NET Core应用程序中,在哪里来使用中间件组件? 怎样来配置ASP.NET Core 应用程序中的中间件组件? 使用中间件组件的例子有哪些? ASP.NET Core应用程序中,中间件组件执行的顺序是? 什么是ASP.NET Core中间件组件? ASP.NET Core中间件组件就是组装

ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析

ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因为WCF中不仅仅只是有SOAP, 它还包含很多如消息安全性,生成WSDL,双工信道,非HTTP传输等. ASP.NET Core 官方推荐大家使用RESTful Web API的解决方案提供网络服务. SOAP 即 Simple Object AccessProtocol 也就是简单对象访问协议.

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

前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World,如果你觉得本篇文章对你有用的话,不妨点个[推荐]. 目录 中间件(Middleware)的作用 中间件的运行方式 中间件(Middleware)和过滤器(Filter)的区别 什么情况我们需要中间件 怎么样自定义自己的中间件 中间件(Middleware)的作用 我们知道,任何的一个web框架都是把http请求封装成

【对比学习】koa.js、Gin与asp.net core——中间件

web框架中间件对比 编程语言都有所不同,各个语言解决同一类问题而设计的框架,确有共通之处,毕竟是解决同一类问题,面临的挑战大致相同,比如身份验证,api授权等等,鄙人对node.js,golang,.net core有所涉猎,对各自的web框架进行学习的过程中发现了确实有相似之处.下面即对node.js的koa.golang的gin与.net core的asp.net core三种不同的web后端框架的中间件做一个分析对比 Node-Koa.js 应用级中间件 //如果不写next,就不会向下

ASP.NET Core 中间件 中间件(Middleware)和过滤器(Filter)的区别

https://www.cnblogs.com/savorboard/p/5586229.html 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World,如果你觉得本篇文章对你有用的话,不妨点个[推荐]. 目录 中间件(Middleware)的作用 中间件的运行方式 中间件(Middleware)和过滤器(Filter)的区别 什么情况我们需要中间件 怎么样自定义自己的