您现在的位置是:网站首页> 编程资料编程资料
ASP.NET Core中间件用法与官方常用中间件介绍_基础应用_
2023-05-24
206人已围观
简介 ASP.NET Core中间件用法与官方常用中间件介绍_基础应用_
一、什么是中间件
我们都知道,任何的一个web框架都是把http请求封装成一个管道,每一次的请求都是经过管道的一系列操作,最终才会到达我们写的代码中。而中间件就是用于组成应用程序管道来处理请求和响应的组件。管道内的每一个组件都可以选择是否将请求转交给下一个组件,并在管道中调用下一个组件之前和之后执行某些操作。请求委托被用来建立请求管道,请求委托处理每一个HTTP请求。
中间件可以认为有两个基本的职责:
- 选择是否将请求传递给管道中的下一个中间件。
- 可以在管道中的下一个中间件前后执行一些工作。
请求委托通过使用IApplicationBuilder类型的Run、Map以及Use扩展方法来配置,并在Startup类中传给Configure方法。每个单独的请求委托都可以被指定为一个内嵌匿名方法,或其定义在一个可重用的类中。这些可以重用的类被称作“中间件”或“中间件组件”。每个位于请求管道内的中间件组件负责调用管道中下一个组件,或适时短路调用链。中间件是一个典型的AOP应用。
ASP.NET Core请求管道由一系列的请求委托所构成,它们一个接着一个的被调用,看下面一张微软官方的中间件请求管道图(图中执行线程按黑色箭头的顺序执行):
中间件短路:每一个委托在下一个委托之前和之后都有机会执行操作。任何委托都能选择停止传递到下一个委托,而是结束请求并开始响应,这就是请求管道的短路,这是一种有意义的设计,因为它可以避免一些不必要的工作。比如说,一个授权(authorization)中间件只有在通过身份验证之后才能调用下一个委托,否则它就会被短路,并返回“Not Authorized”的响应。异常处理委托需要在管道的早期被调用,这样它们就能够捕捉到发生在管道内更深层次出现的异常了。短路可以用下面这张图来表示:
在上图中,我们可以把中间件1认为是身份认证的中间件,HTTP请求发送过来,首先经过身份认证中间件,如果身份认证失败,那么就直接给出响应并返回,不会再把请求传递给下面的中间件2和中间件3.
中间件的执行跟调用的顺序有关,然后在响应时则以相反的顺序返回。
请求在每一步都可能被短路,所以我们要以正确的顺序添加中间件,如异常处理中间件,我们要添加在最开始的地方,这样就能第一时间捕获异常,以及后续中间可能发生的异常,然后最终做处理返回。
我们来看看Configure方法里面提供了哪些中间件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { // 异常中间件 app.UseDeveloperExceptionPage(); } // 路由中间件 app.UseRouting(); // 授权中间件 app.UseAuthorization(); // 终结点中间件 app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
中间件和过滤器的区别
中间件和过滤器都是一种AOP的思想,他们的功能类似,那么他们有什么区别呢?
过滤器更加贴合业务,它关注于应用程序本身,关注的是如何实现业务,比如对输出结果进行格式化,对请求的ViewModel进行数据校验,这时就肯定要使用过滤器了。过滤器是MVC的一部分,它可以拦截到你Action上下文的一些信息,而中间件是没有这个能力的。可以认为过滤器是附加性的一种功能,它只是中间件附带表现出来的特征。中间件是管道模型里重要的组成部分,不可或缺,而过滤器可以没有。
二、中间件常用方法
中间件中定义了Run、Use、Map、MapWhen几种方法,我们下面一一讲解这几种方法。
1、Run方法
我们先来看到Run()方法的定义:
中定义中可以看出:Run()方法中只有一个RequestDelegate委托类型的参数,没有Next参数,所以Run()方法也叫终端中间件,不会将请求传递给下一个中间件,也就是发生了“短路”。看下面的代码:
// Run方法向应用程序的请求管道中添加一个RequestDelegate委托 // 放在管道最后面,终端中间件 app.Run(handler: async context => { await context.Response.WriteAsync(text: "Hello World1\r\n"); }); app.Run(handler: async context => { await context.Response.WriteAsync(text: "Hello World2\r\n"); });
程序运行结果:
可以看到:只输出了中间件1的信息,没有输出中间件2的信息,说明发生了短路。
注意:Run()方法被称为终端中间件,要放在所有中间件的最后面,否则在Run()方法后面的中间件将不会被执行。
2、Use方法
我们先来看看Use()方法的定义:
可以看出:Use方法的参数是一个Func委托,输入参数是一个RequestDelegate类型的委托,返回参数也是一个RequestDelegate类型的委托,这里表示调用下一个中间件,我们在来看看RequestDelegate委托的定义:
可以看出:RequestDelegate是一个委托,有一个HttpContext类型的参数,HttPContext表示Http请求上下文,可以获取请求信息,返回值是Task类型,明白了Use()方法的参数以后,我们写一个自定义的Use()方法:
// 向应用程序的请求管道中添加一个Func委托,这个委托其实就是所谓的中间件。 // context参数是HttpContext,表示HTTP请求的上下文对象 // next参数表示管道中的下一个中间件委托,如果不调用next,则会使管道短路 // 用Use可以将多个中间件链接在一起 app.Use(async (context, next) => { await context.Response.WriteAsync(text: "hello Use1\r\n"); // 调用下一个委托 await next(); }); app.Use(async (context, next) => { await context.Response.WriteAsync(text: "hello Use2\r\n"); // 调用下一个委托 await next(); });
程序运行结果:
我们在上面说过,可以在调用中间件之前和之后做一些工作,看下面的代码:
// 向应用程序的请求管道中添加一个Func委托,这个委托其实就是所谓的中间件。 // context参数是HttpContext,表示HTTP请求的上下文对象 // next参数表示管道中的下一个中间件委托,如果不调用next,则会使管道短路 // 用Use可以将多个中间件链接在一起 app.Use(async (context, next) => { // 解决中文乱码问题 context.Response.ContentType = "text/plain; charset=utf-8"; await context.Response.WriteAsync(text: "中间件1:传入请求\r\n"); // 调用下一个委托 await next(); await context.Response.WriteAsync(text: "中间件1:传出响应\r\n"); }); app.Use(async (context, next) => { await context.Response.WriteAsync(text: "中间件2:传入请求\r\n"); // 调用下一个委托 await next(); await context.Response.WriteAsync(text: "中间件2:传出响应\r\n"); }); app.Run(handler:async context => { await context.Response.WriteAsync(text: "中间件3:处理请求并生成响应\r\n"); });
程序运行结果:
我们可以总结上面代码的执行顺序:
- 请求先到达中间件1,然后输出(中间件1:传入请求)
- 然后中间件1调用next()。next()会调用管道中的中间件2。
- 中间件2输出(中间件2:传入请求)。
- 然后中间件2会调用next()。next()在调用管道中的中间件3。
- 中间件3处理请求并生成响应,不在调用下一个中间件,所以我们看到输出(中间件3:处理请求并生成响应)。
- 这时管理开始发生逆转。
- 此时控制器将交回到中间件2,并将中间件3生成的响应传递给它。中间件2输出(中间件2:传出响应)。
- 最后,中间件2在将控制权交给中间件1。
- 中间件1最后输出(中间件1:传出响应),这就是我们最后看的的结果。
我们知道:Use()方法中有两个参数,next参数表示调用管道中的下一个中间件,如果不调用next,那么也会使管道发生短路,相当于Run()方法,看下面的代码:
// 向应用程序的请求管道中添加一个Func委托,这个委托其实就是所谓的中间件。 // context参数是HttpContext,表示HTTP请求的上下文对象 // next参数表示管道中的下一个中间件委托,如果不调用next,则会使管道短路 // 用Use可以将多个中间件链接在一起 app.Use(async (context, next) => { // 解决中文乱码问题 context.Response.ContentType = "text/plain; charset=utf-8"; await context.Response.WriteAsync(text: "中间件1:传入请求\r\n"); // 调用下一个委托 await next(); await context.Response.WriteAsync(text: "中间件1:传出响应\r\n"); }); app.Use(async (context, next) => { await context.Response.WriteAsync(text: "中间件2:传入请求\r\n"); // 调用下一个委托 await next(); await context.Response.WriteAsync(text: "中间件2:传出响应\r\n"); }); //app.Run(handler:async context => //{ // await context.Response.WriteAsync(text: "中间件3:处理请求并生成响应\r\n"); //}); // Use方法也可以不调用next,表示发生短路 app.Use(async (context, next) => { await context.Response.WriteAsync(text: "中间件3:处理请求并生成响应\r\n"); });
程序运行结果:
可以看出:如果使用Use()方法,不调用next,实现的效果跟使用Run()方法一样,都会使管道发生短路。
3、Map方法
Map作为惯例,将管道分流。Map根据给定请求路径匹配将请求管道分流。如果请求路径以指定路径开始,则执行分支。看一下Map()方法的定义:
可以看到Map方法有两个参数:第一个参数是匹配规则,第二个参数是Action泛型委托,泛型委托参数是IApplicationBuilder类型,和Configure方法的第一个参数类型相同。这就表示可以把实现了Action泛型委托的方法添加到中间件管道中执行。
我们首先定义一个方法,该方法的参数是IApplicationBuilder类型:
////// 自定义方法 /// /// IApplicationBuilder private void HandleMap1(IApplicationBuilder app) { app.Run(handler: async context => { await context.Response.WriteAsync(text: "Hello Map1"); }); } ////// 自定义方法 /// /// IApplicationBuilder private void HandleMap2(IApplicationBuilder app) { app.Run(handler: async context => { await context.Response.WriteAsync(text: "Hello Map2"); }); }
然后看一下使用Map方法的代码:
// Map可以根据匹配的URL来选择执行,简单来说就是根据URL进行分支选择执行 // 有点类似于MVC中的路由 // 匹配的URL:http://localhost:5000/Map1 app.Map(pathMatch: "/Map1", configuration: HandleMap1); // 匹配的URL:http://localhost:5000/Map2 app.Map(pathMatch: "/Map2", configuration: HandleMap2);
运行程序,然后在浏览器地址栏里面输入:http://localhost:5000/Map1,输出结果:
在地址栏里面在输入:http://localhost:5000/Map2,输出结果:
Map还支持嵌套,看下面的代码:
// 嵌套Map app.Map(pathMatch: "/Map1", configuration: App1 => { // App1.Map("/Map2",action=> { action.Run(async context => { await context.Response.WriteAsync("This is /Map1/Map2"); }); }); App1.Run(async context => { await context.Response.WriteAsync("This is no-map"); }); });
访问http://localhost:5000/Map1/123输出结果:
访问http://localhost:5000/Map1输出结果:
访问http://localhost:5000/Map1/Map2输出结果:
提示:
本文由神整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!
相关内容
- ASP.NET Core中使用Swagger_实用技巧_
- .Net解决引用程序集没有强名称报错_实用技巧_
- ASP.NET Core依赖注入详解_实用技巧_
- ASP.NET Core 6.0对热重载的支持实例详解_实用技巧_
- ASP.NET Core中使用Redis实现缓存_实用技巧_
- Entity Framework Core对Web项目生成数据库表_实用技巧_
- Entity Framework Core使用控制台程序生成数据库表_实用技巧_
- ASP.NET MVC+EF实现异步增删改查_实用技巧_
- .NET Core剪裁器背后的技术及工作原理介绍_实用技巧_
- Asp.net core 使用SignalR推送消息过程详解_实用技巧_