.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();

.Net Core MVC理解新管道处理模型、中间件的更多相关文章

  1. ASP.NET Core MVC 之视图组件(View Component)

    1.视图组件介绍 视图组件是 ASP.NET Core MVC 的新特性,类似于局部视图,但它更强大.视图组件不使用模型绑定,并且仅依赖于调用它时所提供的数据. 视图组件特点: 呈块状,而不是整个响应 ...

  2. asp.net core mvc权限控制:在视图中控制操作权限

    在asp.net core mvc中提供了权限验证框架,前面的文章中已经介绍了如何进行权限控制配置,权限配置好后,权限验证逻辑自动就会执行,但是在某些情况下,我们可能需要在代码里或者视图中通过手工方式 ...

  3. ASP.NET Core MVC/WebAPi 模型绑定探索

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  4. asp.net core mvc 管道之中间件

    asp.net core mvc 管道之中间件 http请求处理管道通过注册中间件来实现各种功能,松耦合并且很灵活 此文简单介绍asp.net core mvc中间件的注册以及运行过程 通过理解中间件 ...

  5. ASP.NET Core MVC/WebAPi 模型绑定探索 转载https://www.cnblogs.com/CreateMyself/p/6246977.html

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  6. 【转】ASP.NET Core MVC/WebAPi 模型绑定探索

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  7. 阅读了这三篇文章,你也就基本理解了ASP.NET Core MVC框架的工作原理

    <200行代码,7个对象--让你了解ASP.NET Core框架的本质>让很多读者对ASP.NET Core管道有深刻的理解,知道了ASP.NET Core框架针对每个请求的处理流程.在过 ...

  8. 创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段

    创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段 添加查询功能 本文将实现通过Name查询用户信息. 首先更新GetAll方法以启用查询: public async ...

  9. 008.Adding a model to an ASP.NET Core MVC app --【在 asp.net core mvc 中添加一个model (模型)】

    Adding a model to an ASP.NET Core MVC app在 asp.net core mvc 中添加一个model (模型)2017-3-30 8 分钟阅读时长 本文内容1. ...

随机推荐

  1. CMAKE同时编译C++和CUDA文件

    1. 首先是运行环境 Ubuntu 16.04 G++ 5.4.0 CUDA 8.0 2. 文件结构 cv@cv:~/myproject$ tree src src/ |-- CMakeLists.t ...

  2. 【并发编程】Java并发编程传送门

    本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. [并发编程系列博客传送门](https://www.cnblogs.com/54 ...

  3. 直击KubeCon北美峰会:开发者们不得不关注的10大项目

    今天晚上8:30,第三期在线培训<如何通过Istio进行金丝雀发布>将准时开播,内容包括Istio原理.金丝雀发布.在Rancher中启用Istio,报名及观看链接:https://www ...

  4. Thread.Sleep线程休眠-小白向

    try { Thread.sleep(); } catch (InterruptedException e) { } 首先这段代码的作用是使当前进程沉睡2S,展现给用户的结果就是画面维持两秒,有个“正 ...

  5. CODING 敏捷看板全新上线,助力研发管理可视化升级

    在服务企业研发团队的过程中,我们发现不少团队碰到了类似的问题: 团队成员声称完成了自己的大部分任务,但团队实际交付的需求却寥寥无几? 由于某些问题导致工序一直处于等待状态,怎么识别和处理这些延迟? 成 ...

  6. leetcode菜鸡斗智斗勇系列(4)--- 单一数字的乘积和总合的减法

    1.原题: https://leetcode.com/problems/subtract-the-product-and-sum-of-digits-of-an-integer/ Given an i ...

  7. python函数中参数的传递

    Python唯一支持的参数传递方式是『共享传参』(call by sharing)多数面向对象语言都采用这一模式,包括Ruby.Smalltalk和Java(Java的引用类型是这样,基本类型按值传递 ...

  8. 【Cef编译】 CefSharp编译失败,检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”

    编译CefSharp生成后一个libcef_dll_wrapper.lib时,供CefSharp使用.结果CefSharp编译的时候报错.遇到以下异常: libcef_dll_wrapper.lib( ...

  9. logback日志文件位置动态指定

    logback日志文件位置动态指定 参考:https://stackoverflow.com/questions/19518843/logback-configuration-via-jvm-argu ...

  10. Redis缓存NoSQL

    下面是一些关于Redis比较好的文章,因为篇幅较大,我就将其折叠起来了.不太喜欢分不同的笔记去记载,除非真的很多很多.所以本文不仅要对Redis做简单的介绍,还要分别介绍Redis中的五种结构,并会贴 ...