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

.Net Core MVC理解新管道处理模型、中间件

程序员文章站 2022-06-29 21:21:32
.Net Core中间件官网:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.0 ASP.Net请求管道: 请求最终会由一个具体的HttpHandler处理(page/as ......

.net core中间件官网:

asp.net请求管道:

.Net Core MVC理解新管道处理模型、中间件

 

 

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

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

asp.net core:

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

.Net Core MVC理解新管道处理模型、中间件

 

 

  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处理一切。

.Net Core MVC理解新管道处理模型、中间件

 

 委托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();