定制.NET 6.0的Middleware中间件

大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进。

在本文中,我们将学习中间件,以及如何使用它进一步定制应用程序。我们将快速学习中间件的基础知识,然后探讨如何使用它做的一些特殊事情。
本文涵盖的主题包括:

  • 中间件简介

  • 编写自定义中间件

  • 中间件的潜力

  • 如何使用中间件

本章所处的位置,如下图所示:
6aaaa94758d504db5b488b3fb14503cb.png

技术准备

我们使用控制台、shell或Bash终端先创建一个ASP.NET Core MVC应用程序,然后切换到工作目录:

dotnet new web -n MiddlewaresDemo -o MiddlewaresDemo

然后用VS打开项目:

cd MiddlewaresDemo code .

注意在.NET 6.0中,web项目模板发生了变化。Microsoft引入了minimal API,项目模板默认使用minimal API。

中间件简介

大多数人可能已经知道中间件是什么,但有些人可能不知道,即使你已经在使用ASP.NET Core有一段时间了。我们一般不需要详细了解中间件实例,因为它们大多隐藏在扩展方法后面,例如UseMvc()、UseAuthentication()、UseDeveloperExceptionPage()等。每次在Configure方法中,我们默认将隐式地使用至少一个或更多个中间件组件。

中间件组件是处理请求管道的一段代码。我们可以将请求流程想象成一串管道,每次请求调用,都会返回一个响应。中间件负责创建回声——它操纵请求上下文,加工处理、叠加逻辑、丰富信息。
6b00b2dc4d1f186766f93e82f10030e3.png

中间件组件按配置顺序执行。配置的第一个中间件组件是第一个执行的组件。我们可以把中间件看成回旋镖,出去的时候第一个执行,回来的时候最后一个执行。

在ASP.NET Core web应用程序,如果客户端请求的是图像或任何其他静态文件,StaticFileMiddleware将负责查找该资源,如果找到该资源,则返回该资源。如果没有,这个中间件除了调用下一个之外什么都不做。

MvcMiddleware组件检查请求的资源,将其映射到已配置的路由,执行控制器,创建视图,并返回HTML或Web API结果。如果MvcMiddleware没有找到匹配的控制器,它无论如何都会返回一个结果——通常是一个404状态的结果,这就是为什么MvcMiddleware是最后配置的中间件。

异常处理中间件通常是配置的第一批的中间件之一,不是因为它是第一个执行的,而是因为它是最后一个执行的。异常处理验证结果,并以客户端友好的方式在浏览器中显示可能的异常。以下过程描述了运行时发生的500错误状态:

var builder = WebApplication.CreateBuilder(args); 
var app = builder.Build(); 
app.MapGet("/", () => "Hello World!"); 
app.Run();

在ASP.NET Core 6.0,Microsoft引入了minimal API,它简化了应用配置,并隐藏了许多默认配置,比如隐式的using声明,因此,在头部我们看不到任何using语句,以上就是我们看到的ASP.NET Core 6.0中的Program.cs 文件内容。
在这里,lambda中间件绑定到默认路由,只有一句简单的“Hello World!”响应流。这个特殊的中间件会终止管道并返回响应内容。因此,它是最后一个运行的中间件。

下面我们把app.MapGet()做个替换,如下所示:

app.Use(async (context, next) =>{     await context.Response.WriteAsync("===");     await next();     await context.Response.WriteAsync("==="); 
}); 
app.Use(async (context, next) => { await context.Response.WriteAsync(">>>>>> ");     await next();     await context.Response.WriteAsync(" <<<<<<");
}); 
app.Run(async context => { await context.Response.WriteAsync("Hello World!"); 
});

这里调用两个app.Use()方法,并且创建了两个lambda中间件,除了做简单的处理外,中间件还调用了它们的后继组件,每个中间件的调用链很明确很清晰。在调用下一个中间件之前,处理实际的请求,在调用下个中间件之后,处理响应。以上就是管道的工作机制。
如果现在运行程序(使用dotnet run)并在浏览器中打开URL,我们应该会看到这样的纯文本结果

===>>>>>> Hello World! <<<<<<===

不知道您理解了没?如果理解了,我们往下学习,看看如何使用这个概念向请求管道添加一些附加功能。

编写自定义中间件

中间件可以说是ASP.NET Core的基座,在请求期间执行的所有逻辑都基于此机制。因此,我们可以使用它向web添加自定义功能。在下面案例,我们希望找出通过请求管道的每个请求的执行时间:

我们可以在调用下一个中间件之前创建并启动秒表,然后在调用下个中间件之后停止测量执行时间,如下所示:

app.Use(async (context, next) => {     var s = new Stopwatch();     s.Start();     //其他操作 await next();     s.Stop(); //结束度量     var result = s.ElapsedMilliseconds;     //统计耗时     await context.Response.WriteAsync($"耗时:{result} 秒。"); 
});

记得为System.Diagnostics添加using语句。
之后,我们将经过的毫秒返回到响应流。
如果您编写的中间件组件很多,Program.cs将变得非常混乱。所以大多数中间件组件将被编写为独立的类,如下所示:

using System.Diagnostics; 
public class StopwatchMiddleware {    private readonly RequestDelegate _next;     public StopwatchMiddleware(RequestDelegate next)  {  _next = next;  }     public async Task Invoke(HttpContext context) {         var s = new Stopwatch();         s.Start();         //其他操作          await _next(context);         s.Stop(); //结束度量         var result = s.ElapsedMilliseconds;         //统计耗时     await context.Response.WriteAsync($"耗时:{result} 秒。");    }  
}

在Invoke方法中的,我们获得构造函数和当前上下文获得要执行的下一个中间件组件。

注意:
中间件在应用程序启动时初始化,构造函数在应用程序生命周期内仅运行一次。另一方面,每个请求调用一次Invoke方法。
要使用此中间件,您可以使用一个通用的UseMiddleware方法:

app.UseMiddleware<StopwatchMiddleware>();

然而,更优雅的方法是创建一个封装此调用的扩展方法:

public static class StopwatchMiddlewareExtension {     public static IApplicationBuilder  UseStopwatch(this IApplicationBuilder app)     {         app.UseMiddleware<StopwatchMiddleware>();         return app;    }}

然后就可以这样使用:

app.UseStopwatch();

这样,您可以通过请求管道向ASP.NET Core应用程序提供其他功能。中间件中提供了整个HttpContext。这样,您可以使用中间件操纵请求和响应。

例如,AuthenticationMiddleware尝试从请求中收集用户信息。如果找不到任何信息,它将通过向客户端发送特定的响应来请求信息。如果它找到,它会将其添加到请求上下文中,并以这种方式将其提供给整个应用程序。

中间件的潜力

使用中间件还可以做许多其他事情。例如,可以将请求管道拆分为两个或多个管道,我们将在这里讨论如何做到这一点。

使用/map分支管道

下一段代码显示了如何基于特定路径创建请求管道的分支:

app.Map("/map1", app1 => {     // 其他中间件     app1.Run(async context =>     {         await context.Response.WriteAsync("Map Test 1");     }); 
}); 
app.Map("/map2", app2 => {     // 其他中间件     app2.Run(async context => {         await context.Response.WriteAsync("Map Test 2");     }); 
}); 
// 其他中间件

/map1路径是一个特定的分支,它在内部继续请求管道,/map2与此相同。这两个map都有自己内部的中间件配置。所有其他未指定的路径都遵循该主分支。

使用MapWhen分支管道

还有一个MapWhen方法可以根据条件分支管道,而不是根据路径分支:

public void Configure(IApplicationBuilder app) {     app.MapWhen(context =>context.Request.Query.ContainsKey("分支"),         app1 => {            // 其他中间件           app1.Run(async context =>  {  await context.Response.WriteAsync( "MapBranch Test"); });     });     //其他中间件     app.Run(async context =>    { await context.Response.WriteAsync("Hello non-Map.");     });
}

使用中间件构造条件

我们一般可以根据配置值创建条件,或者根据请求上下文的属性创建条件。在前面的示例中,我们使用了查询字符串属性作为条件。当然,你也可以使用HTTP标头、表单属性或请求上下文的任何其他属性。

如果需要,还可以嵌套map以创建子分支和孙分支。
我们再看下健康检查中间件,ASP.NET Core HealthCheck API的工作原理如下:
首先,它使用MapWhen指定要使用的端口,然后,它使用Map设置HealthCheck API路径(如果未指定端口则使用Map)。最后,使用了HealthCheckMiddleware。我们看下面的代码示例:

private static void UseHealthChecksCore(IApplicationBuilder app, PathString path, int? port, object[] args) 
{     if (port == null)    {         app.Map(path, b => b.UseMiddleware<HealthCheckMiddleware>(args)); }     else  {        app.MapWhen(c => c.Connection.LocalPort == port,b0 => b0.Map(path, b1 =>b1.UseMiddleware<HealthCheckMiddleware>(args)));     }; 
}

这里,我们可以使用Map或MapWhen分别基于特定路径或特定条件提供特殊的API或资源。
接下来,让我们看看如何在更新版本的ASP.NET Core中使用终止中间件组件。

在ASP.NET Core 3.0及更高版本中使用中间件

ASP.NET Core 3.0及更高版本,有两种新的中间件,它们被称为UseRoutingUseEndpoints

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {     if (env.IsDevelopment())     {  app.UseDeveloperExceptionPage();     }     app.UseRouting();     app.UseEndpoints(endpoints =>  {         endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello  World!");         });     }); 
}

第一个是使用路由的中间件UseRouting,另一个是访问地址的UseEndpoints

这是新的端点路由。以前,路由是MVC的一部分,它只适用于MVC、Web API和基于MVC的框架。然而在ASP.NET Core 3.0及更高版本,路由不再是MVC框架中的一部分。现在,MVC和其他框架都可以被映射到特定的路由或端点。
在前面的代码段中,GET请求被映射到页面根URL。在下一个代码片段中,MVC被映射到路由模式,RazorPages被映射到基于RazorPage的特定文件结构的路由:

app.UseEndpoints(endpoints => {     endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); endpoints.MapRazorPages(); 
});

现在已经没有UseMvc方法了,即使它仍然存在并在IApplicationBuilder对象级别上工作,以防止现有代码中断。现在,激活ASP.NET Core功能的方法更为精细。

  • Areas for MVC and web APIendpoints.MapAreaControllerRoute(...);

  • MVC and web APIendpoints.MapControllerRoute(...);

  • Blazor server-sideendpoints.MapBlazorHub(...);

  • SignalRendpoints.MapHub(...);

  • Razor Pagesendpoints.MapRazorPages(...);

  • Health checksendpoints.MapHealthChecks(...);

这些是ASP最常用的新Map方法。
还有很多方法可以定义回退地址,比如将路由和HTTP方法映射到代理,以及中间件组件。
你可以创建适用于所有请求的中间件,例如StopWatchMiddleware,你也可以编写中间件以在特定路径或路由上工作,例如创建一个Map方法,以将其映射到该路由。

注意事项
不再建议在中间件内部处理路由。相反,您应该使用新的地址路由。使用这种方法,中间件更加通用,它可以通过单一的配置就可以在多个路由上工作。

重写终止中间件

接下来,我们创建小型虚拟中间件,将应用程序状态写入特定路由。在此示例中,没有自定义路由处理:

namespace MiddlewaresSample; 
public class AppStatusMiddleware {     private readonly RequestDelegate _next;     private readonly string _status;public AppStatusMiddleware(RequestDelegate next, string status)     {        _next = next;         _status = status;    }     public async Task Invoke(HttpContext context)  {         await context.Response.WriteAsync($"Hello {_status}!");     } 
}

我们需要做的是在IEndpointRouteBuilder对象上编写一个扩展方法。此方法将路由模式作为可选参数,并返回IEndpointConventionBuilder对象以启用跨域资源共享(CORS)、身份验证或路由的其他条件。

现在,我们应该添加一个扩展方法,以便更容易地使用中间件:

public static class MapAppStatusMiddlewareExtension {     public static IEndpointConventionBuilder MapAppStatus(this IEndpointRouteBuilder routes, string pattern = "/", string name = "World") {         var pipeline = routes.CreateApplicationBuilder().UseMiddleware<AppStatusMiddleware>(name).Build();         return routes.Map(pattern, pipeline).WithDisplayName("AppStatusMiddleware");     } 
}

完成后,我们可以使用MapAppStatus方法将其映射到特定路线:

app.UseRouting(); 
app.UseEndpoints(endpoints => {     endpoints.MapGet("/", () => "Hello World!");     endpoints.MapAppStatus("/status", "Status"); 
});

现在,我们可以通过输入以下地址在浏览器中调用路由: http://localhost:5000/status

总结

大多数ASP.NET Core功能基于中间件,在本章中,我们学习了中间件的工作原理以及如何创建自己的中间件组件来扩展ASP.NET框架。我们还学习了如何使用新路由向自定义的终止中间件添加路由。

在下一章中,我们将了解ASP.NET Core中的新端点路由,它允许我们以简单灵活的方式创建自己的托管端点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/280008.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

删除microsoft_如何从您的Microsoft帐户中删除设备

删除microsoftWhen you sign into Windows 8 or 10 using your Microsoft account (and other Microsoft devices, like an Xbox), those devices become associated with your account. If you want to remove an old device you’ve gotten rid of, you’ll have to pay a vi…

线程的语法 (event,重要)

Python threading模块 2种调用方式 直接调用 12345678910111213141516171819import threadingimport timedef sayhi(num): #定义每个线程要运行的函数print("running on number:%s" %num)time.sleep(3)if __name__ __main__:t1 threading.Thread(targetsayhi,args(…

求最大值和下标值

本题要求编写程序&#xff0c;找出给定的n个数中的最大值及其对应的最小下标&#xff08;下标从0开始&#xff09;。 输入格式: 输入在第一行中给出一个正整数n&#xff08;1<n≤10&#xff09;。第二行输入n个整数&#xff0c;用空格分开。 输出格式: 在一行中输出最大值及…

windows应用商店修复_如何修复Windows应用商店中的卡死下载

windows应用商店修复Though it’s had its share of flaky behavior since being introduced in Windows 8, the Windows Store has gotten more reliable over time. It still has the occasional problems, though. One of the more irritating issues is when an app update…

新冠病毒中招 | 第二天

今天跟大家分享我个人感染奥密克戎毒株第二天的经历和感受。早上7点多自然醒来&#xff0c;已经没有四肢乏力的感觉&#xff0c;但是身体的本能还是告诉我不愿意动弹。由于第一天躺着睡了一天&#xff0c;确实是躺得腰酸背疼的。起床量了一下体温36.4正常&#xff0c;决定今天不…

icloud 购买存储空间_如何释放iCloud存储空间

icloud 购买存储空间Apple offers 5 GB of free iCloud space to everyone, but you’ll run up against that storage limit sooner than you’d think. Device backups, photos, documents, iCloud email, and other bits of data all share that space. Apple为每个人提供5 …

基于LAMP实现web日志管理查看

前言&#xff1a;日志是一个重要的信息库&#xff0c;如何高效便捷的查看系统中的日志信息&#xff0c;是系统管理员管理系统的必备的技术。实现方式&#xff1a;1、将日志存储于数据库。2、采用LAMP架构&#xff0c;搭建PHP应用&#xff0c;通过web服务访问数据库&#xff0c;…

WPF效果第二百零七篇之EditableSlider

前面简单玩耍一下快速黑白灰效果; 今天又玩了一下ZoomBlurEffect,来看看最终实现的效果:1、ps和cs文件都在Shazzam中,咱们自己随意玩耍;今天主角是下面这位:2、来看看自定义控件布局(TextBox、Slider、ToggleButton)&#xff1a;3、点击编辑按钮,我就直接偷懒了:private void E…

使用MyQ打开车库门时如何接收警报

Chamberlain’s MyQ technology is great for opening and closing your garage door remotely with your smartphone, but you can also receive alerts whenever your garage door opens and closes (as well as receive alerts when it’s been open for an extended amount…

mac 防火墙禁止程序联网_如何允许应用程序通过Mac的防火墙进行通信

mac 防火墙禁止程序联网If you use a Mac, chances are you might not even realize that OS X comes with a firewall. This firewall helps ensure unauthorized app and services can’t contact your computer, and prevents intruders from sniffing out your Mac on a ne…

WPF-22 基于MVVM员工管理-02

我们接着上一节&#xff0c;这节我们实现crud操作&#xff0c;我们在EmployeeViewMode类中新增如下成员&#xff0c;并在构造函数中初始化该成员code snippetpublic EmployeeViewMode() {employeeService new EmployeeService();BindData();Employee new Employee();AddComma…

linux 3

-- Linux -- 开心的一天 vi   所有的 unix like 系统都会内置 vi 文本编辑器 vim  较多使用的,可以主动的以字体颜色辨别语法的正确性&#xff0c;方便程序设计 vi/vim 的使用 -- 命令模式&#xff08;Command mode&#xff09; 输入模式&#xff08;Insert mode&#x…

从零开始搭建一个简单的ui自动化测试框架02(pytest+selenium+allure)

二、先搭一个架子 在我还是小白连py语法都不太熟悉的时候&#xff0c;经常在网上看关于自学ui自动化测试的博客&#xff0c;最熟悉的套路莫过于先给你介绍一下selenium的各个api&#xff0c;然后写一套代码去登陆微博或者百度什么的&#xff0c;但我今天不愿意这么写&#xff0…

什么是Adobe Lightroom,我需要它吗?

Adobe Photoshop Lightroom confuses a lot of new photographers. It has Photoshop in the name, but it isn’t Photoshop? What gives? Adobe Photoshop Lightroom使许多新摄影师感到困惑。 它的名称是Photoshop&#xff0c;但不是Photoshop吗&#xff1f; 是什么赋予了&…

jquery中的serializeArray方法的使用

转载于:https://blog.51cto.com/11871779/2359556

机器学习(一)—— 线性回归

机器学习&#xff08;一&#xff09;—— 线性回归 目录 0. 回归&#xff08;Regression&#xff09;的由来 1. 回归关系 2. 线性回归的整体思路 &#xff08;1&#xff09;根据数据提出假设模型 &#xff08;2&#xff09;求解参数 1&#xff09;梯度下降法 2&#xff09;正规…

Java EE启示录

前言 最近的这段时间一直在学习Java EE&#xff0c;刚刚完成了从0到1的蜕变&#xff0c;所以顺便整理一下我所了解到的Java EE&#xff0c;给刚入门学习的新人一些头绪&#xff0c;而所谓“启示录”&#xff0c;就是这个意思。 一.Java EE是什么&#xff1f; Java EE&#xff0…

又到年末“团建”!某企业员工吐槽:这真是一场噩梦……

这是头哥侃码的第270篇原创2022年即将结束&#xff0c;很多公司又到了一年一度的年末团建。前天晚上&#xff0c;之前的同事找我聊天&#xff0c;说他们公司因为最近疫情的原因&#xff0c;准备把年末“团建”放到春节后进行。但是计划的时间是2月份的某个周末&#xff0c;并且…

天梯 L2 这是二叉搜索树吗?

L2-004 这是二叉搜索树吗&#xff1f; &#xff08;25 分&#xff09;一棵二叉搜索树可被递归地定义为具有下列性质的二叉树&#xff1a;对于任一结点&#xff0c; 其左子树中所有结点的键值小于该结点的键值&#xff1b;其右子树中所有结点的键值大于等于该结点的键值&#xf…

Cactiz中文版安装使用

#----------------------------------------------------------# # > 红色字体 -特指煮酒个人所见。加粗则为需要重点注意。 ## > 蓝色加粗 -特指与本文相关人员&#xff0c;包括参与修正的朋友。 ## > 煮酒品茶 -Http://cwtea.blog.51cto.com # #----------…