zoukankan      html  css  js  c++  java
  • .Net Core MVC理解新管道处理模型、中间件

    .Net Core中间件官网:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.0

    ASP.Net请求管道:

       请求最终会由一个具体的HttpHandler处理(page/ashx/mvc httphandler---action)。但是还有多个步骤,被封装成事件,可以注册扩展,IHttpModule,提供了非常优秀的扩展。

      但是这样有一个缺陷,那就是太多管闲事了,一个http请求最核心的是IHttpHandler,其他的Cookie、Session、Session、BeginRequest、EndRequest、MapRequestHandler、授权等,不一定非得有这些请求的事件的逻辑,但是写死了,就必须得有,默认认为那些步骤是必须有的,因为跟框架的设计思想有关。.Net Framework入门简单精通难,因为框架大包大揽,全家桶式,WebForm里面拖一个控件然后就可以撸代码了,一个项目就出来了,所以精通也难,也要付出代价,就是包袱比较重,不能轻装前行。

    ASP.Net Core:

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

      ASP.NET Core是一套全新的平台,已经不再向前兼容,设计更追求组件化,追求高性能,没有全家桶,那么ASP.NET Core是怎么搭建请求管道的呢?默认情况,管道只有一个404。然后你也可以增加请求的处理,这就是以前的Handler,只包含业务处理环节,其他的就是中间件,MiddleWare。

    1、Run 终结式  只是执行,没有去调用Next  ,一般作为终结点。所谓Run终结式注册,其实只是一个扩展方法,最终还不是得调用Use方法,

    app.Run(async (HttpContext context) =>
    {
        await context.Response.WriteAsync("Hello World Run");
    });
    app.Run(async (HttpContext context) =>
    {
        await context.Response.WriteAsync("Hello World Run Again");
    });

    2、Use表示注册动作  不是终结点  ,执行next,就可以执行下一个中间件   如果不执行,就等于Run

    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Hello World Use1 <br/>");
        await next();
        await context.Response.WriteAsync("Hello World Use1 End <br/>");
    });
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Hello World Use2 Again <br/>");
        await next();
    });

    UseWhen可以对HttpContext检测后,增加处理环节;原来的流程还是正常执行的

     app.UseWhen(context =>
     {
         return context.Request.Query.ContainsKey("Name");
     },
     appBuilder =>
     {
         appBuilder.Use(async (context, next) =>
         {
             await context.Response.WriteAsync("Hello World Use3 Again Again Again <br/>");
             await next();
         });
     });

    app.Use(),没有调用next(),那就是终结点,跟Run一样

    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Hello World Use3  Again Again <br/>");
        //await next();
    });

    3、Map:根据条件指定中间件  指向终结点,没有Next,最好不要在中间件里面判断条件选择分支;而是一个中间件只做一件事儿,多件事儿就多个中间件

    app.Map("/Test", MapTest);
    app.Map("/Bingle", a => a.Run(async context =>
    {
        await context.Response.WriteAsync($"This is Bingle Site");
    }));
    app.MapWhen(context =>
    {
        return context.Request.Query.ContainsKey("Name");
        //拒绝非chorme浏览器的请求  
        //多语言
        //把ajax统一处理
    }, MapTest);

      IApplicationBuilder 应用程序的组装者,RequestDelegate:传递一个HttpContext,异步操作下,不返回;也就是一个处理动作,Use(Func<RequestDelegate, RequestDelegate> middleware) 委托,传入一个RequestDelegate,返回一个RequestDelegate。ApplicationBuilder里面有个容器IList<Func<RequestDelegate, RequestDelegate>> _components,Use就只是去容器里面添加个元素。最终会Build()一下, 如果没有任何注册,就直接404处理一切,

     foreach (var component in _components.Reverse())//反转集合  每个委托拿出来
    {
        app = component.Invoke(app);
        //委托3-- 404作为参数调用,返回 委托3的内置动作--作为参数去调用委托(成为了委托2的参数)--循环下去---最终得到委托1的内置动作---请求来了HttpContext---
    }

      IApplicationBuilder build之后其实就是一个RequestDelegate,能对HttpContext加以处理,默认情况下,管道是空的,就是404;可以根据你的诉求,任意的配置执行,一切全部由开发者自由定制,框架只是提供了一个组装方式

    其实,中间件可以这样写。

    Func<RequestDelegate, RequestDelegate> middleware = next =>
    {
        return new RequestDelegate(async context =>
                        {
                            await context.Response.WriteAsync("<h3>This is Middleware1 start</h3>");
                            await Task.CompletedTask;
                            await next.Invoke(context);//RequestDelegate--需要context返回Task
                            await context.Response.WriteAsync("<h3>This is Middleware1 end</h3>");
                        });
    };
    app.Use(middleware);

    每次都要这么麻烦,去定义一个Func<RequestDelegate,RequestDelegate>,然后去使用吗?我们可以进化一点点

     app.Use(next =>
     {
         System.Diagnostics.Debug.WriteLine("this is Middleware1");
         return new RequestDelegate(async context =>
         {
             await context.Response.WriteAsync("<h3>This is Middleware1 start</h3>");
             await next.Invoke(context);
             await context.Response.WriteAsync("<h3>This is Middleware1 end</h3>");
         });
     });
    
     app.Use(next =>
     {
         System.Diagnostics.Debug.WriteLine("this is Middleware2");
         return new RequestDelegate(async context =>
         {
             await context.Response.WriteAsync("<h3>This is Middleware2 start</h3>");
             await next.Invoke(context);
             await context.Response.WriteAsync("<h3>This is Middleware2 end</h3>");
         });
     });
     app.Use(next =>
     {
         System.Diagnostics.Debug.WriteLine("this is Middleware3");
         return new RequestDelegate(async context =>
         {
             await context.Response.WriteAsync("<h3>This is Middleware3 start</h3>");
             //await next.Invoke(context);//注释掉,表示不再往下走
             await context.Response.WriteAsync("<h3>This is Middleware3 end</h3>");
         });
     });

    执行的结果,顺序为:

    <h3>This is Middleware1 start</h3>
    <h3>This is Middleware2 start</h3>
    <h3>This is Middleware3 start</h3>
    
    <h3>This is Middleware3 end</h3>
    <h3>This is Middleware2 end</h3>
    <h3>This is Middleware1 end</h3>

    和以前ActionFilter是不是很像,是一个俄罗斯套娃,我比较喜欢说成洋葱模型。其实是因为源码中,将IList<Func<RequestDelegate,RequestDelegate>> _components,将_components.Reverse()使集合反转了。

    那中间件的代码,下面这种写法不好吗?

     app.Use(async (context, next) =>
     {
         //Do work that doesn't write to the Response
         await next.Invoke();
         //Do logging or other work that doesn't write to the Response
     });
    
     app.Run(async context =>
     {
         await context.Response.WriteAsync("Hello from 2nd delegate.");
     });

    ApplicationBuilder里面有个容器IList<Func<RequestDelegate,RequestDelegate>> _components。Use的时候就只是去容器里面添加个元素,最终Build()一下,如果没有任何注册,就直接404处理一切。

     委托3---404作为参数调用,返回委托3的内置动作---作为参数去调用委托(成为了委托2的参数)---循环下去,最终得到委托1的内置动作,请求来了HttpContext,IApplicationBuilder,build之后其实就是一个RequestDelegate,能对HttpContext加以处理,默认情况下,管道是空的,就是404,可以根据你的诉求,任意的配置执行,一切全有开发者自由定制,框架只是提供了一个组装方式。

    中间件里面的逻辑可以封装到一个类中去:

     public class FirstMiddleWare
     {
         private readonly RequestDelegate _next;
    
         public FirstMiddleWare(RequestDelegate next)
         {
             this._next = next;
         }
    
    
         public async Task Invoke(HttpContext context)
         {
             await context.Response.WriteAsync($"{nameof(FirstMiddleWare)},Hello World1!<br/>");
    
             await _next(context);
    
             await context.Response.WriteAsync($"{nameof(FirstMiddleWare)},Hello World2!<br/>");
         }
    
     }

    在使用的时候:

    app.UseMiddleware<FirstMiddleWare>();

    其实,我们可以再升级一点点,使用扩展方法,将这个类中的逻辑作为IApplicationBuilder的扩展方法。

    public static class MiddleExtend
    {
        public static IApplicationBuilder UseFirstMiddleWare(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<FirstMiddleWare>();
        }
    }

    在使用的时候就简单多了

    app.UseFirstMiddleWare();
  • 相关阅读:
    HTML5全屏API在FireFox/Chrome中的显示差异(转)
    过程需要参数 '@statement' 为 'ntext/nchar/nvarchar'
    程序员面试zhongdian
    select into #T from ,insert into #T select ,insert into #T exec
    Log4Net Layout使用以及扩展
    jdbc报java.lang.ClassNotFoundException: com.mysql.jdbc.Drive
    eclipse的maven项目中找不到Maven Dependencies
    elfinder中通过DirectoryStream.Filter实现筛选隐藏目录(二)
    一个好用的字符过滤,差异匹配补丁的扩展库,各语言版本
    java调用c/c++代码简单实现以及遇见的坑
  • 原文地址:https://www.cnblogs.com/taotaozhuanyong/p/11600770.html
Copyright © 2011-2022 走看看