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

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

本文涵盖的主题包括:

  • 中间件简介
  • 编写自定义中间件
  • 中间件的潜力
  • 如何使用中间件

本章所处的位置,如下图所示:

技术准备

我们使用控制台、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方法中,我们默认将隐式地使用至少一个或更多个中间件组件。

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

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

在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中的新端点路由,它允许我们以简单灵活的方式创建自己的托管端点。

定制.NET 6.0的Middleware中间件的更多相关文章

  1. ASP.NET MVC随想录——创建自定义的Middleware中间件

    经过前2篇文章的介绍,相信大家已经对OWIN和Katana有了基本的了解,那么这篇文章我将继续OWIN和Katana之旅——创建自定义的Middleware中间件. 何为Middleware中间件 M ...

  2. 创建自定义的Middleware中间件

    创建自定义的Middleware中间件 阅读目录 何为Middleware中间件 使用Inline方式注册Middleware 使用Inline+ AppFunc方式注册Middleware 定义原生 ...

  3. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...

  4. Startup 和 Middleware(中间件)

    Startup 和 Middleware(中间件) ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Con ...

  5. 定制一个winCE5.0操作系统

    定制一个winCE5.0操作系统 2009-04-01 09:01:14|  分类: winCE|字号 订阅     定制一个操作系统并模拟器上运行,需要以下几个步骤: STEP 1:用Platfor ...

  6. Django中Middleware中间件

    Django中Middleware中间件 1 Middleware中间件概述 django中间middleware实质就是一个类,django会根据自己的规则在合适的时机执行中间件相应的方法.实际上当 ...

  7. Laravel5.1 Middleware中间件(初级)

    中间件?什么鬼? 大家第一次接触这个词都会有这么个疑问,但它其实没那么神秘. 一句话就可以解释它:过滤HTTP请求专用机制. 为什么要使用中间件? 过滤HTTP请求是可以写在别的地方,比如说控制器中 ...

  8. golang程序设计:Go middleware中间件以及Gin 中间件分析

    先从业务开发角度出发,来逐渐引出中间件. 一.刚开始时业务开发 开始业务开发时,业务需求比较少. 当我们最开始进行业务开发时,需求不是很多. 第一个需求产是品向大家打声招呼:"hello w ...

  9. Django分析之Middleware中间件

    写了几周的脚本,今天终于开始接触web框架了~学习Python的web框架,那么Django就几乎是必修课了,这次的工作是先打打下手,主要的任务是在setting中添加版本号,在渲染静态css,js的 ...

  10. Express ( MiddleWare/中间件 路由 在 Express 中使用模板引擎 常用API

    A fast, un-opinionated, minimalist web framework for Node.js applications. In general, prefer simply ...

随机推荐

  1. 老杜MySql——34道作业题

    老杜MySql链接:https://www.bilibili.com/video/BV1Vy4y1z7EX?p=132 本次随笔主要来源于老杜MySql讲解视频后面的作业题,加上个人的一些理解,以及整 ...

  2. P1099 [NOIP2007 提高组] 树网的核 (树的直径)

    题目的意思就是在直径上找一段距离不超过s的路径,使该路径的偏心距最小. 求出直径之后,显然我们可以用双指针扫描一段合法路径.设u1,u2...ut是直径上的点,d[ui]表示从ui出发能到达的最远距离 ...

  3. .NET 6 跨服务器联表查询

    一.大家是否有这个需求 1.跨品种查询 :比如 MYSQL和一个SQLSERVER进行联表查询 ,或者SQLITE和MYSQL进行联表查询 2.跨服务器查询 : A服务器和B服务器查询 如果想同时支持 ...

  4. 3.ElasticSearch系列之Docker本地部署

    对于之前的部署方式一般用于生产环境,而对于学习而言Docker方式快速部署就好了,本示例在window10环境下进行. 1. Docker使用Elasticsearch 需要对vm.max_map_c ...

  5. 银行ATM存取款系统(C语言实现)

    这里使用的运行工具是DEV C++.老铁们一定要看仔细了.是DEV C++ 仅供借鉴:这个是大一时期写的.大四的时候整理了一下(本人C语言学的也不太好).肯定很多不足和存在漏洞的地方.仅供借鉴.仅供借 ...

  6. Windows 环境搭建 PostgreSQL 逻辑复制高可用架构数据库服务

    本文主要介绍 Windows 环境下搭建 PostgreSQL 的主从逻辑复制,关于 PostgreSQl 的相关运维文章,网络上大多都是 Linux 环境下的操作,鲜有在 Windows 环境下配置 ...

  7. Windows7下驱动开发与调试体系构建——3.调试体系概述

    目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html 调试体系概述 0.什么是自建调试体系? 就是复写windows的调试api,使得调试 ...

  8. 深度学习环境搭建常用网址、conda/pip命令行整理(pytorch、paddlepaddle等环境搭建)

    前言:最近研究深度学习,安装了好多环境,记录一下,方便后续查阅. 1. Anaconda软件安装 1.1 Anaconda Anaconda是一个用于科学计算的Python发行版,支持Linux.Ma ...

  9. 编辑距离(Minimum Edit Distance)

    编辑距离(Minimum Edit Distance,MED),也叫 Levenshtein Distance.他的含义是计算字符串a转换为字符串b的最少单字符编辑次数.编辑操作有:插入.删除.替换( ...

  10. ES6 学习笔记(四)基本类型Number

    1.数值 1.1 .JavaScript数值的特点 不区分整数值和浮点数值. 所有数值均用浮点数值表示. 采用IEEE-754标准定义的64位浮点数格式表示. 整数在实际操作时(如数组索引),则是基于 ...