上海网站推荐做米业的企业网站
web/
2025/10/4 23:27:35/
文章来源:
上海网站推荐,做米业的企业网站,北京大学 讣告,个人微信公众号收费吗本文通过一张GIF动图来继续聊一下ASP.NET Core的请求处理管道#xff0c;从管道的配置、构建以及请求处理流程等方面做一下详细的研究。#xff08;ASP.NET Core系列目录#xff09;一、概述上文说到#xff0c;请求是经过 Server监听处理成httpContextApplication… 本文通过一张GIF动图来继续聊一下ASP.NET Core的请求处理管道从管道的配置、构建以及请求处理流程等方面做一下详细的研究。ASP.NET Core系列目录一、概述 上文说到请求是经过 Server监听处理成httpContextApplication处理生成Response。 这个Application的类型RequestDelegate本质是 public delegate Task RequestDelegate (HttpContext context); 即接收HttpContext并返回Task 它是由一个个中间件 FuncRequestDelegate, RequestDelegate middleware 嵌套在一起构成的。它的构建是由ApplicationBuilder完成的先来看一下这个ApplicationBuilderpublic class ApplicationBuilder : IApplicationBuilder{ private readonly IListFuncRequestDelegate, RequestDelegate _components new ListFuncRequestDelegate, RequestDelegate(); public IApplicationBuilder Use(FuncRequestDelegate, RequestDelegate middleware) { _components.Add(middleware); return this; } public RequestDelegate Build() { RequestDelegate app context { context.Response.StatusCode 404; return Task.CompletedTask; }; foreach (var component in _components.Reverse())20 { app component(app); } return app; }} ApplicationBuilder有个集合 IListFuncRequestDelegate, RequestDelegate _components 和一个用于向这个集合中添加内容的 Use(FuncRequestDelegate, RequestDelegate middleware) 方法通过它们的类型可以看出来它们是用来添加和存储中间件的。现在说一下大概的流程调用startupFilters和_startup的Configure方法调用其中定义的多个UseXXX进一步调用ApplicationBuilder的Use方法将一个个中间件middleware按照顺序写入上文的集合_components记住这个_components。定义了一个 context.Response.StatusCode 404 的RequestDelegate。将集合_components颠倒一下, 然后遍历其中的middleware一个个的与新创建的404 RequestDelegate 连接在一起组成一个新的RequestDelegate即Application返回。 这个最终返回的RequestDelegate类型的Application就是对HttpContext处理的管道了这个管道是多个中间件按照一定顺序连接在一起组成的startupFilters先不说以我们非常熟悉的Startup为例它的Configure方法默认情况下已经依次进行了UseBrowserLink、UseDeveloperExceptionPage、UseStaticFiles、UseMvc了等方法请求进入管道后请求也会按照这个顺序来经过各个中间件处理首先进入UseBrowserLink然后UseBrowserLink会调用下一个中间件UseDeveloperExceptionPage依次类推到达UseMVC后被处理生成Response开始逆向返回再依次反向经过这几个中间件正常情况下请求到达MVC中间件后被处理生成Response开始逆向返回而不会到达最终的404这个404是为了防止其他层未配置或未能处理的时候的一个保险操作。 胡扯两句这个管道就像一座塔话说唐僧路过金光寺去扫金光塔从前门进入第一层开始扫然后从前门的楼梯进入第二层、第三层、第四层然后从第四层的后门扫下来直至后门出去却不想妖怪没处理好 被唐僧扫到了第五层顶层去发现佛宝被奔波儿灞和霸波尔奔偷走了大喊悟空悟空佛宝被妖怪偷走啦404... 下面就以这4个为例通过一个动图形象的描述一下整个过程 一个“中规中矩”的管道就是这样构建并运行的通过上图可以看到各个中间件在Startup文件中的配置顺序与最终构成的管道中的顺序的关系下面我们自己创建几个中间件体验一下然后再看一下不“中规中矩”的长了杈子的管道。二、自定义中间件 先仿照系统现有的写一个public class FloorOneMiddleware { private readonly RequestDelegate _next; public FloorOneMiddleware(RequestDelegate next) { _next next; } public async Task InvokeAsync(HttpContext context) { Console.WriteLine(FloorOneMiddleware In); //Do Something //To FloorTwoMiddleware await _next(context); //Do Something Console.WriteLine(FloorOneMiddleware Out); } }这是塔的第一层进入第一层后的 //Do Something 表示在第一层需要做的工作 然后通过 _next(context) 进入第二层再下面的 //Do Something 是从第二层出来后的操作。同样第二层调用第三层也是一样。再仿写个UseFloorOne的扩展方法public static class FloorOneMiddlewareExtensions { public static IApplicationBuilder UseFloorOne(this IApplicationBuilder builder) { Console.WriteLine(Use FloorOneMiddleware); return builder.UseMiddlewareFloorOneMiddleware(); } }这样在Startup的Configure方法中就也可以写 app.UseFloorOne(); 将这个中间件作为管道的一部分了。 通过上面的例子仿照系统默认的中间件完成了一个简单的中间件的编写这里也可以用简要的写法直接在Startup的Configure方法中这样写app.Use(async (context,next) { Console.WriteLine(FloorThreeMiddleware In); //Do Something //To FloorThreeMiddleware await next.Invoke(); //Do Something Console.WriteLine(FloorThreeMiddleware Out);});同样可以实现上一种例子的工作但还是建议按照那样的写法在Startup这里体现的简洁并且可读性好的多。复制一下第一种和第二种的例子形成如下代码public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseFloorOne(); app.UseFloorTwo(); app.Use(async (context,next) { Console.WriteLine(FloorThreeMiddleware In); //Do Something //To FloorThreeMiddleware await next.Invoke(); //Do Something Console.WriteLine(FloorThreeMiddleware Out); }); app.Use(async (context, next) { Console.WriteLine(FloorFourMiddleware In); //Do Something await next.Invoke(); //Do Something Console.WriteLine(FloorFourMiddleware Out); }); if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler(/Home/Error); } app.UseStaticFiles(); app.UseMvc(routes { routes.MapRoute( name: default, template: {controllerHome}/{actionIndex}/{id?}); }); }运行一下看日志CoreMiddleware Use FloorOneMiddlewareCoreMiddleware Use FloorTwoMiddlewareCoreMiddleware Hosting environment: DevelopmentCoreMiddleware Content root path: C:\Users\FlyLolo\Desktop\CoreMiddleware\CoreMiddlewareCoreMiddleware Now listening on: http://localhost:10757CoreMiddleware Application started. Press CtrlC to shut down.CoreMiddleware info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]CoreMiddleware Request starting HTTP/1.1 GET http://localhost:56440/ CoreMiddleware FloorOneMiddleware InCoreMiddleware FloorTwoMiddleware InCoreMiddleware FloorThreeMiddleware InCoreMiddleware FloorFourMiddleware InCoreMiddleware info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]CoreMiddleware Executing action method CoreMiddleware.Controllers.HomeController.Index (CoreMiddleware) with arguments ((null)) - ModelState is ValidCoreMiddleware info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1]CoreMiddleware Executing ViewResult, running view at path /Views/Home/Index.cshtml.CoreMiddleware info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]CoreMiddleware Executed action CoreMiddleware.Controllers.HomeController.Index (CoreMiddleware) in 9896.6822msCoreMiddleware FloorFourMiddleware OutCoreMiddleware FloorThreeMiddleware OutCoreMiddleware FloorTwoMiddleware OutCoreMiddleware FloorOneMiddleware OutCoreMiddleware info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]CoreMiddleware Request finished in 10793.8944ms 200 text/html; charsetutf-8可以看到前两行的Use FloorOneMiddleware和Use FloorTwoMiddleware是将对应的中间件写入集合_components而中间件本身并未执行然后10至12行是依次经过我们自定义的例子的处理第13-18就是在中间件MVC中的处理了找到并调用对应的Controller和View然后才是19-22的逆向返回 最终Request finished返回状态200 这个例子再次验证了请求在管道中的处理流程。那么我们试一下404的情况 把Configure方法中除了自定义的4个中间件外全部注释掉再次运行//上面没变化 省略CoreMiddleware FloorOneMiddleware InCoreMiddleware FloorTwoMiddleware InCoreMiddleware FloorThreeMiddleware InCoreMiddleware FloorFourMiddleware InCoreMiddleware FloorFourMiddleware OutCoreMiddleware FloorThreeMiddleware OutCoreMiddleware FloorTwoMiddleware OutCoreMiddleware FloorOneMiddleware OutCoreMiddleware info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]CoreMiddleware Request finished in 218.7216ms 404可以看到MVC处理的部分没有了因为该中间件已被注释而最后一条可以看到系统返回了状态404。 那么既然MVC可以正常处理请求没有进入404 我们怎么做可以这样呢是不是不调用下一个中间件就可以了 试着把FloorFour改一下app.Use(async (context, next) { Console.WriteLine(FloorFourMiddleware In); //await next.Invoke(); await context.Response.WriteAsync(Danger!); Console.WriteLine(FloorFourMiddleware Out);});再次运行查看输出和上文的没有啥太大改变 只是最后的404变为了200 网页上的“404 找不到。。”也变成了我们要求输出的Danger!, 达到了我们想要的效果。但一般情况下我们不这样写ASP.NET Core 提供了Use、Run和Map三种方法来配置管道。三、Use、Run和Map Use上面已经用过就不说了对于上面的问题 一般用Run来处理Run主要用来做为管道的末尾例如上面的可以改成这样app.Run(async (context) { await context.Response.WriteAsync(Danger!);
}); 因为本身他就是作为管道末尾也就省略了next参数虽然用use也可以实现 但还是建议用Run。 Map static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, ActionIApplicationBuilder configuration); pathMatch用于匹配请求的path 例如“/Home”, 必须以“/”开头 判断path是否是以pathMatch开头。若是则进入 ActionIApplicationBuilder configuration) 这个参数是不是长得很像startup的Configure方法 这就像进入了我们配置的另一个管道它是一个分支如下图图2做个例子app.UseFloorOne();app.Map(/Manager, builder { builder.Use(async (context, next) { await next.Invoke(); }); builder.Run(async (context) { await context.Response.WriteAsync(Manager.); });});app.UseFloorTwo(); 进入第一层后 添加了一个Map 作用是当我们请求 localhost:56440/Manager/index 这样的地址的时候是不是有点像Area 会进入这个Map创建的新分支 结果也就是页面显示Manager. 不会再进入下面的FloorTwo。若不是“/Manager”开头的 这继续进入FloorTwo。虽然感觉这个Map灵活了我们的管道配置 但这个只能匹配path开头的方法太局限了不着急 我们看一下MapWhen。 Map When MapWhen方法就是一个灵活版的Map它将原来的PathMatch替换为一个 FuncHttpContext, bool predicate 这下就开放多了它返回一个bool值现在举个栗子随便改一下app.MapWhen(context {return context.Request.Query.ContainsKey(XX);}, builder { //...TODO...} 当根据请求的参数是否包含“XX”的时候进入这个分支。 从图2可知一旦进入分支是无法回到原分支的 如果只是想在某种情况下进入某些中间件但执行完后还可以继续后续的中间件怎么办呢对比MapWhenUse也有个UseWhen。 UseWhen 它和MapWhen一样当满足条件的时候进入一个分支在这个分支完成之后再继续后续的中间件当然前提是这个分支中没有Run等短路行为。app.UseWhen(context {return context.Request.Query.ContainsKey(XX);}, builder { //...TODO...}四、IStartupFilter 我们只能指定一个Startup类作为启动类那么还能在其他的地方定义管道么 文章开始的时候说到构建管道的时候会调用startupFilters和_startup的Configure方法调用其中定义的多个UseXXX方法来将中间件写入_components。自定义一个StartupFilter实现IStartupFilter的Configure方法用法和Startup的Configure类似不过要记得最后调用 next(app) 。public class TestStartupFilter : IStartupFilter{ public ActionIApplicationBuilder Configure(ActionIApplicationBuilder next) { return app { app.Use(async (context, next1) { Console.WriteLine(filter.Use1.begin); await next1.Invoke(); Console.WriteLine(filter.Use1.end); }); next(app); }; }}在复制一个去startup的ConfigureServices注册一下public void ConfigureServices(IServiceCollection services){ services.AddMvc(); services.AddSingletonIStartupFilter,TestStartupFilter(); services.AddSingletonIStartupFilter, TestStartupFilter2();}这样的配置就生效了现在剖析一下他的生效机制。回顾一下WebHost的BuildApplication方法private RequestDelegate BuildApplication(){ //....省略 var startupFilters _applicationServices.GetServiceIEnumerableIStartupFilter(); ActionIApplicationBuilder configure _startup.Configure; foreach (var filter in startupFilters.Reverse()) { configure filter.Configure(configure); } configure(builder); return builder.Build();仔细看这段代码其实这和构建管道的流程非常相似对比着说一下首先通过GetService获取到注册的IStartupFilter集合startupFilters类比_components然后获取Startup的Configure类比404的RequestDelegate翻转startupFiltersforeach它并且与Startup的Configure链接在一起。上文强调要记得最后调用 next(app)这个是不是和 next.Invoke() 类似。 是不是感觉和图一的翻转拼接过程非常类似是不是想到了拼接先后顺序的问题。对比着管道构建后中间件的执行顺序体会一下后这时应该可以想到各个IStartupFilter和Startup的Configure的执行顺序了吧。没错就是按照依赖注入的顺序TestStartupFilterTestStartupFilter2Startup。}原文地址 https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_8.html.NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/87035.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!