欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

asp.net core 系列 15 中间件

程序员文章站 2022-09-30 18:34:06
一.概述 中间件(也叫中间件组件)是一种装配到应用管道以处理请求和响应的软件。 每个组件:(1)选择是否将请求传递到管道中的下一个组件;(2)可以在管道中的下一个组件之前和之后执行工作。 请求委托用于生成请求管道。 请求委托会处理每个 HTTP 请求。使用以下方法配置请求委托:Run, Map, U ......

一.概述

  中间件(也叫中间件组件)是一种装配到应用管道以处理请求和响应的软件。 每个组件:(1)选择是否将请求传递到管道中的下一个组件;(2)可以在管道中的下一个组件之前和之后执行工作。

  请求委托用于生成请求管道。 请求委托会处理每个 http 请求。使用以下方法配置请求委托:run,  map, use扩展方法。可以将单个请求委托作为匿名方法(称为内联中间件in-line middleware) 或者可以在可重用类中定义。这些可重用的类和内联匿名方法是中间件,也称为中间件组件。请求管道中的每个中间件组件负责调用管道中的下一个组件,或使管道短路。

  (1) run

      //将终端中间件委托添加到应用程序的请求管道中。
      public static class runextensions
      {
          public static void run(this iapplicationbuilder app, requestdelegate handler);
      }

   (2) map

      // 根据给定请求路径的匹配对请求管道进行分支。
      public static class mapextensions
      {
          public static iapplicationbuilder map(this iapplicationbuilder app, pathstring pathmatch, action<iapplicationbuilder> configuration);
      }

  (3) use

      // 提供配置应用程序请求的机制    
      public interface iapplicationbuilder
      {
          //....
          // 将中间件委托添加到应用程序的请求管道中。
          iapplicationbuilder use(func<requestdelegate, requestdelegate> middleware); 
      }

 

  1.1 使用 iapplicationbuilder 创建中间件管道

    在startup. configure方法中,使用iapplicationbuilder来创建中间件管理。每一个use开头的扩展方法将一个中间件添加到iapplicationbuilder请求管道中。使用use扩展方法来配置请求委托。每个use的中间件类似如下声明:

    public static iapplicationbuilder use[middleware] (this iapplicationbuilder app )
      public static iapplicationbuilder use[middleware] (this iapplicationbuilder app , action<t>)

    asp.net core 请求管道包含一系列请求委托,依次调用。 下图演示了这一概念。 沿黑色箭头执行。

asp.net core 系列 15 中间件

         在startup. configure代码中,一系列use请求委托中间件如下所示:

           app.usehttpsredirection();
           app.usestaticfiles();
           app.usecookiepolicy();
           app.usemvc();

    委托可以决定不将请求传递给下一个委托(中间件),这就是对请求管道进行短路。通常需要短路,因为这样可以避免不必要的工作。

    下面示例 是一个最简单的 asp.net core 应用程序,用run方法配置请求委托,设置单个委托处理处理所有请求。此案例不包括实际的请求管道。相反,调用单个匿名函数以响应每个 http 请求。并用委托终止了管道。

    public class startup
    {
        public void configure(iapplicationbuilder app)
        {
            app.run(async context =>
            {
                await context.response.writeasync("hello, world!");
            });
        }
    }

asp.net core 系列 15 中间件

    下面示例用use方法将多个请求委托链接在一起,next 参数表示管道中的下一个委托。 可通过不调用 next 参数使管道短路。

            app.use(async (context, next) =>
            {
              //调用下一个委托(app.run)
                await next.invoke();
            });

            app.run(async context =>
            { 
                await context.response.writeasync("hello, world!");
            });

    

  1.2 中间件顺序

     向 startup.configure 方法添加中间件组件的顺序定义了针对请求调用这些组件的顺序,以及响应的相反顺序。 此排序对于安全性、性能和功能至关重要。以下 startup.configure 方法将为常见应用方案添加中间件组件:  

         (1) 异常/错误处理

         (2) http 严格传输安全协议

         (3) https 重定向

         (4) 静态文件服务器

         (5) cookie 策略实施

         (6) 身份验证

         (7) 会话

         (8) mvc

public void configure(iapplicationbuilder app)
{
    if (env.isdevelopment())
    {
        // when the app runs in the development environment:
        //   use the developer exception page to report app runtime errors.
        //   use the database error page to report database runtime errors.
        app.usedeveloperexceptionpage();
        app.usedatabaseerrorpage();
    }
    else
    {
        // when the app doesn't run in the development environment:
        //   enable the exception handler middleware to catch exceptions
        //     thrown in the following middlewares.
        //   use the http strict transport security protocol (hsts)
        //     middleware.
        app.useexceptionhandler("/error");
        app.usehsts();
    }

    // use https redirection middleware to redirect http requests to https.
    app.usehttpsredirection();

    // return static files and end the pipeline.
    app.usestaticfiles();

    // use cookie policy middleware to conform to eu general data 
    // protection regulation (gdpr) regulations.
    app.usecookiepolicy();

    // authenticate before the user accesses secure resources.
    app.useauthentication();

    // if the app uses session state, call session middleware after cookie 
    // policy middleware and before mvc middleware.
    app.usesession();

    // add mvc to the request pipeline.
    app.usemvc();
}

    (1) useexceptionhandler 是添加到管道的第一个中间件组件。 该异常处理程序中间件可捕获稍后调用中发生的任何异常。

    (2) usestaticfiles 静态文件中间件,应该在管道的早期调用。这样它就可以处理请求和短路,而不需要遍历其余组件。静态文件中间件不提供授权检查。 它提供的任何文件,包括wwwroot下的文件,都是公开可访问的。

    (3) useauthentication 身份验证中间件。未经身份验证的请求不会短路,但只有在特定的razor页面或mvc控制器操作之后,才会发生授权(和拒绝)。

 

  1.3  use、run 和 map

     配置 http 管道可以使用use、run 和 map,但各方法针对构建的中间件作用不同:

      (1) use[middleware]中间件负责调用管道中的下一个中间件,也可使管道短路(即不调用 next 请求委托)。

      (2) run[middleware]是一种约定,一些中间件组件可能会公开在管道末端运行的run[middleware]方法。

      (3) map扩展用作约定来创建管道分支, map*创建请求管道分支是基于给定请求路径的匹配项。

        public void configure(iapplicationbuilder app, ihostingenvironment env)
        {
            app.map("/map1",handlemaptest1);
            app.map("/map2", handlemaptest2);
            //其它请求地址
            app.run(async context =>
            {
                await context.response.writeasync("hello from non-map delegate. <p>");
            });
        }

        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");
            });
        }

asp.net core 系列 15 中间件

    map  还支持嵌套,下面的示例中,请求访问/level1/level2a 和 /level1/level2b时进行不同逻辑处理:

app.map("/level1", level1app => {
    level1app.map("/level2a", level2aapp => {
        // "/level1/level2a" processing
    });
    level1app.map("/level2b", level2bapp => {
        // "/level1/level2b" processing
    });
});

     

  1.4 mapwhen

    mapwhen 基于url给定谓词的结果创建请求管道分支。 func<httpcontext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。 在以下示例中,谓词用于检测查询字符串变量 branch 是否存在,如果存在使用新分支(handlebranch)。

        public void configure(iapplicationbuilder app, ihostingenvironment env)
        {
            //func<httpcontext, bool> predicate, action<iapplicationbuilder> configuration
            app.mapwhen(context => context.request.query.containskey("branch"), handlebranch);
           
            //非匹配branch其它请求地址
            app.run(async context =>
            {
                await context.response.writeasync("hello from non-map delegate. <p>");
            });
        }

        private static void handlebranch(iapplicationbuilder app)
        {
             app.run(async context =>
            {
                var branchver = context.request.query["branch"];
                await context.response.writeasync("map test 1");
            });
        }

asp.net core 系列 15 中间件

 

二. 编写中间件    

  上面演示在请求管道中使用use,map,run方法,来委托处理每个 http 请求就是中间件。通常中间件会封装在类中,并且通过扩展方法公开。下面示例是如何编写一个中间件组件。处理逻辑是该中间件通过查询字符串设置当前请求的区域性。

    /// <summary>
    /// 自定义中间件实现类
    /// </summary>
    public class requestculturemiddleware
    {
        //using microsoft.aspnetcore.http
        private readonly requestdelegate _next;

      
      /// <summary>
      /// 程序启动时调用
      /// </summary>
      /// <param name="next"></param>
    public requestculturemiddleware(requestdelegate next)
        {
            this._next = next;
        }

    
      /// <summary>
      ///每个页面请求时自动调用,方法按约定命名,必需是invoke或invokeasync
      /// </summary>
      /// <param name="context"></param>
      /// <returns></returns>
    public async task invokeasync(httpcontext context)
        {
            var culturequery = context.request.query["culture"];
            if (!string.isnullorwhitespace(culturequery))
            {
                //using system.globalization;
                var culture = new cultureinfo(culturequery);

                cultureinfo.currentculture = culture;
                cultureinfo.currentuiculture = culture;

            }
            // call the next delegate/middleware in the pipeline
            await _next(context);
        }
    }

    /// <summary>
    /// 通过扩展方法公开中间件 
    /// </summary>
    public static class requestculturemiddlewareextensions
    {
        public static iapplicationbuilder userequestculture(this iapplicationbuilder builder)
        {
            //在管道中添加一个use的中间件
            return builder.usemiddleware<requestculturemiddleware>();
        }
    }
        public void configure(iapplicationbuilder app)
        {
            //调用中间件
            app.userequestculture();

            app.run(async (context) =>
            {
                await responseasync(context);
            });

        }

        private  async  task responseasync(httpcontext context)
        {
            context.response.contenttype = "text/html; charset=utf-8";
            await context.response.writeasync(
                    //打印当前显示的语言
                    $"hello { cultureinfo.currentculture.displayname }"
                    );
        }

asp.net core 系列 15 中间件

  2.1 请求依赖项

    由于中间件是在应用启动时构造的(实例),而不是在每个请求时的,因此在每个请求过程中,中间件构造函数使用的作用域生命周期服务,不会在每个请求期间与其他依赖注入类型共享。如果必须在中间件和其他类型之间共享一个范围服务,请将这些服务添加到 invoke 方法的签名。 invoke 方法可接受由 di 填充的参数

public class custommiddleware
{
    private readonly requestdelegate _next;

    public custommiddleware(requestdelegate next)
    {
        _next = next;
    }

    // imyscopedservice is injected into invoke
    public async task invoke(httpcontext httpcontext, imyscopedservice svc)
    {
        svc.myproperty = 1000;
        await _next(httpcontext);
    }
}

 

  参考文献:

    官方文档:asp.net core 中间件