ASP.NET Core自定义中间件的方式详解

目录
  • 1.委托形式
  • 2.强类型中间件
    • 2.1.定义中间件的依赖
    • 2.2.定义中间件类型
  • 3.基于约定的中间件
    • 3.1.约定规则
    • 3.2.应用实现
  • 总结

ASP.NET Core应用本质上,其实就是由若干个中间件构建成的请求处理管道。管道相当于一个故事的框架,而中间件就相当于故事中的某些情节。同一个故事框架采用不同的情节拼凑,最终会体现出不同风格的故事。而我们的ASP.NET Core应用也正是如此,同一管道采用不同的中间件组合,最终也会呈现出不同的应用形态。

从上述的概念种可以看出,中间件在ASP.NET Core应用有着举足轻重的地位。虽然ASP.NET Core为我们提供了一组丰富的内置中间件,但有些时候我们可能会需要自定义一些中间件,将其穿插到管道中,以便满足我们特定业务场景的需求,所以本文将介绍3种方式来满足自定义中间件的需求。

1.委托形式

在应用程序代码中,我们可以从用于注册中间件的Use方法中看出,所谓管道中的中间件其实就是一种委托类型的对象,这个具体的委托对象体现为“Fun<RequestDelegate,RequestDelegate>”。

从Fun<RequestDelegate,RequestDelegate>委托的定义可以看出,该委托类型的入参和返回值都是一个RequestDelegate委托类型的对象。RequestDelegate委托类型其实就是管道在代码中的体现形式,该委托类型承载很多关于请求响应的重要信息,定义如下:

public delegate Task RequestDelegate(HttpContext context);

Fun<RequestDelegate,RequestDelegate>委托中,入参的RequestDelegate对象表示由上一个中间件构建的管道,返回值的RequestDelegate对象表示:将当前中间件基于上一个管道处理后生成的新管道。由于中间件体现为一个Fun<RequestDelegate,RequestDelegate>委托对象,那么这就代表我们可以定义一个与该委托具有一致声明的方法作为自定义中间件的方式。具体的代码实现方式如下:

//创建应用
var app = WebApplication.Create(args);

//转换获得应用建造者
IApplicationBuilder appBuilder = app;

//注册自定义的中间件
appBuilder.Use(SayHi);

//运行应用
app.Run();

//定义为Fun<RequestDelegate,RequestDelegate>类型的方法
static RequestDelegate SayHi(RequestDelegate  request)
    => httpContext => httpContext.Response.WriteAsync("Hello");

上面的代码是在一个原始的控制台程序中编写的,并且自行进行了主机应用的构建。在代码中定义了一个和Fun<RequestDelegate,RequestDelegate>委托签名一致的SayHi方法,并以此方法作为中间件进行了引用。虽然这是一个可行的方式,但在实际开发的工作场景中,其实很少会使用委托形式作为自定义中间件的方式。在此处之所以演示这种形式,主要是为了表面中间件本质是一个委托,并且不管通过什么形式去定义中间件,它最终都会体现为一个Fun<RequestDelegate,RequestDelegate>委托对象。

2.强类型中间件

在实际的开发过程中,基本上都会将自定义的中间件定义为一个具体类型,而对于使用强类型的中间件而言,则我们定义的中间件类型必须实现IMiddleware接口。既然通过一个具体类型来定义中间件,类型在使用上则势必会与其他类型产生依赖关联性,那么对于中间件类型中依赖服务的实例化,框架则要求我们使用依赖注入的方式。接下来我们将通过代码示例演示如何定义一个强类型的中间件。

2.1.定义中间件的依赖

下面代码定义的类型是我们预先为中间件类型定义的依赖项,ISeasonTips接口类型的作用主要是,根据不同月份获取对应的季节,并输出对应季节的注意事项,其中SeasonTips类型是接口的默认实现。

public interface ISeasonTips
    {
        string Prompt(DateTimeOffset time);
    }

    public class SeasonTips : ISeasonTips
    {
        //根据不同月份提示季节注意事项
        public string Prompt(DateTimeOffset time) => time.Month switch
        {
            var h when h >= 3 && h <= 5 => "春天到了,早晚温差比较大,要注意别感冒。",
            var h when h >= 6 && h <= 8 => "夏天到了,天气炎热,要注意别防嗮。",
            var h when h >= 9 && h <= 11 => "秋天到了,天气干燥,要注意多喝水。",
            _ => "冬天到了,天气寒冷,要注意防寒保暖。"

        }; //END Prompt()  

    }

2.2.定义中间件类型

下面的代码中,我们定义了一个名为SeasonMiddleware的中间件类型,并实现IMiddleware接口。该中间件的处理请求的逻辑在InvokeAsync方法中,该方法调用其依赖类型的Prompt方法,根据当前时间获取当前季节的注意事项进行输出。在该调用该方法后,我们还对InvokeAsync的另一个参数:“RequestDelegate类型的委托对象”进行了调用,以便执行管道中的下一个中间件。另外,对于中间件依赖的类型ISeasonTips,我们将其定义在构造函数的参数列表上,以便依赖注入容器提供相应的实例。

/// <summary>
    /// 强类型中间件
    /// </summary>
    public class SeasonMiddleware : IMiddleware
    {
        //依赖类型,通过构造函数进行依赖注入
        private readonly ISeasonTips _seasonTips;
        public SeasonMiddleware(ISeasonTips seasonTips)
        {
            _seasonTips = seasonTips;
        }

        //调用依赖的“季节提示类型”,根据当前时间获取当前季节的注意事项,并进行响应输出
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            await context.Response.WriteAsync(_seasonTips.Prompt(DateTimeOffset.Now));

            //调用管道中的下一个中间件
            await next(context);
        }  // END InvokeAsync()

    }  // END Class

在下面的代码中我们对自定义的“强类型中间件”进行了应用。由于“强类型中间件”的实例以及依赖都是由依赖注入容器提供的,所以不仅要对依赖的服务进行注册,还要对自身的中间件类型进行服务注册。在服务注册之后,我们使用WebApplication对象的UseMiddleware<SeasonMiddleware>扩展方法,将该中间件添加到应用程序的请求管道中。由于在该中间件后没有其他中间件的处理,所以我们通过调用Run扩展方法注册了管道末端的中间件,以便结束当前请求,将响应输出到客户端。

using dotNet6Demo;

//创建“应用建造者”
var builder = WebApplication.CreateBuilder(args);

//服务注册
builder.Services.AddSingleton<ISeasonTips, SeasonTips>().AddSingleton<SeasonMiddleware>();

//构建应用
var app = builder.Build();

//引用强类型中间件
app.UseMiddleware<SeasonMiddleware>();

//末端的中间件
app.Run(async (context) =>
{
    await context.Response.WriteAsync("请求结束");
});

//运行应用
app.Run();

到目前为止,结合本示例以上的3个步骤,启动运行程序就可以验证自定义强类型中间件的效果了。

3.基于约定的中间件

对于ASP.NET的开发者而言,基于约定的编程模式应该不会陌生。例如在ASP.NET MVC框架中,“Action”默认查找视图就有一种基于约定的规则,即“Action”首先会在Views目录中查找与当前“Controller”同名的目录,然后在该目录中查找与“Action”同名的视图文件。这种基于约定的设计方式,在自定义中间件领域也同样使用到了,即基于约定的中间件。

3.1.约定规则

基于约定的中间件它不必像强类型中间件那样,必须实现IMiddleware接口或继承某些基类,它只用按照框架约定的方式定义中间件类型即可,具体的约定规则如下:

  1. 中间件类型必须要定义为一个公共的、可供外界实例化的类型,静态类型无效;
  2. 构造函数的参数中必须包含RequestDelegate类型,如果存在依赖类型则也必须包含在构造函数中;

必须定义InvokeAsync或Invoke方法,方法签名为:public Task Invoke(HttpContext context);

对以上的约定进行一个补充说明:构造函数的参数列表要包含依赖的类型,是为了依赖注入容器对依赖类型提供实例;RequestDelegate参数具有传递性,表示由后续中间件构建的管道,当前中间件利用它将请求转交给后续管道进行处理。InvokeAsync或Invoke方法主要是代表中间件在管道中处理请求的逻辑。

3.2.应用实现

下面我们在“强类型中间件”示例的基础上,根据约定规则将SeasonMiddleware类型改造为“基于约定的中间件”,代码如下:

/// <summary>
    /// 基于约定的中间件
    /// </summary>
    public class SeasonMiddleware
    {
        private readonly ISeasonTips _seasonTips;
        private readonly RequestDelegate _next;

        public SeasonMiddleware(ISeasonTips seasonTips, RequestDelegate next)
        {
            _seasonTips = seasonTips;
            _next = next;
        }

        //调用依赖的“季节提示类型”,根据当前时间获取当前季节的注意事项,并进行响应输出
        public async Task InvokeAsync(HttpContext context)
        {
            await context.Response.WriteAsync(_seasonTips.Prompt(DateTimeOffset.Now));
            //调用管道中的下一个中间件
            await _next(context);

        }  // END InvokeAsync()

    }  // END Class

在中间件引用方面,“基于约定的中间件”同样可以使用“app.UseMiddleware<SeasonMiddleware>()”的方式进行引用,但是在此我们介绍一种较为常用的方式,就是将自定义中间件的引用方式进行封装,将其作为IApplicationBuilder类型的扩展方法来使用,扩展方法定义的代码如下:

public static class SeasonMiddlewareExtensions
    {
        public static IApplicationBuilder UseSeason(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<SeasonMiddleware>();
        }
    }

接下来在示例应用方面,将其调整为使用“基于约定中间件”的形式,并使用扩展方法引用中间件。

using dotNet6Demo;

//创建“应用建造者”
var builder = WebApplication.CreateBuilder(args);

//服务注册
builder.Services.AddSingleton<ISeasonTips, SeasonTips>();

//构建应用
var app = builder.Build();

//通过自定义扩展方法 引用中间件
app.UseSeason();

//末端的中间件
app.Run(async (context) =>
{
    await context.Response.WriteAsync("请求结束");
});

//运行应用
app.Run();

在对以上中间件应用方面,我们能可以看出“基于约定的中间件”类型并没有进行服务注册,而“强类型中间件”类型却进行了服务注册,这是因为两者在提供实例的方式上有着本质的区别。

“基于约定的中间件”的实例是在应用启动时便可提供的,并且只能指定的一个固定的生命周期模式“Singleton”,所以该类型中间件具有和应用程序一样的生存期,直到应用程序关闭才会释放。

“强类型中间件”的实例并不是在应用启动时提供的,它需要根据服务注册时指定的生命周期,来决定创建提供的时机。例如“强类型中间件”注册的生命周期为“Scoped”,那么依赖注入容器会根据客户端的请求实时创建中间件的实例,请求处理完成后才会被释放。

总结

中间件的使用地位在ASP.NET Core中绝对是毋庸置疑的,那么对于较为复杂的项目而言,自定义中间件的需求绝对是“绕不开的弯”,所以我们必须掌握自定义中间件的方式。

本文介绍了3种可以实现自定义ASP.NET Core中间件的方式。其中第一种并不推崇作为实战运用的手段,其目的是为了让我们明白:中间件最终的体现形式其实就是一个委托对象,该委托对象承载了请求上下信息,并具有传递性。在实际的使用中,我们可以在第二种和第三种中进行选择,也就是“强类型中间件”和“基于约定的中间件”,从两者的特点上来看,“基于约定的中间件”在使用方面会更加的方便,但是其生命周期模式只能局限于Singleton。而“强类型中间件”可以通过服务注册为中间件实例指定任意的生命周期模式,相比更加灵活。

对于具体的选择,我们想我们还是交给我们实际的运用场景。

如果想了解更多关于自定义 ASP.NET Core 中间件的方式,可以访问如下的官方文档:

写入自定义 ASP.NET Core 中间件 | Microsoft Docs

到此这篇关于ASP.NET Core自定义中间件的方式的文章就介绍到这了,更多相关ASP.NET Core自定义中间件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Asp.Net Core7 preview4限流中间件新特性详解

    目录 前言 UseRateLimiter尝鲜 本地测试 ConcurrencyLimiter源码 获取令牌 尝试获取令牌核心逻辑 令牌获取失败后进入等待队列 归还令牌 总结 前言 限流是应对流量暴增或某些用户恶意攻击等场景的重要手段之一,然而微软官方从未支持这一重要特性,AspNetCoreRateLimit这一第三方库限流库一般作为首选使用,然而其配置参数过于繁多,对使用者造成较大的学习成本.令人高兴的是,在刚刚发布的.NET 7 Preview 4中开始支持限流中间件. UseRateLim

  • ASP.NET Core基础之中间件

    什么是ASP.NET Core Middleware? ASP.NET Core中间件组件是被组装到应用程序管道中以处理HTTP请求和响应的软件组件(从技术上来说,组件只是C#类). ASP.NET Core应用程序中的每个中间件组件都执行以下任务. 选择是否将 HTTP 请求传递给管道中的下一个组件.这可以通过在中间件中调用下一个 next() 方法实现. 可以在管道中的下一个组件之前和之后执行工作. 在ASP.NET Core中,已经有很多内置的中间件组件可供使用,您可以直接使用它们. 如果

  • ASP.NET Core使用自定义日志中间件

    这个日志框架使用的是ASP.NET Core的NLog,用来记录每次请求信息和返回信息. 1.首先创建一个Web应用项目,我选择的是MVC模板: 2.使用NuGet添加Microsoft.Extensions.Logging和NLog.Extensions.Logging 3.修改Configure方法: public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFac

  • ASP.NET Core中间件

    目录 1.前言 2.使用中间件 2.1 Run 2.2 Use 2.3 Map和MapWhen 3.顺序 4.编写中间件(重点) 4.1中间件类 4.2中间件扩展方法 5.按每次请求创建依赖注入(DI) 1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以处理请求和响应的组件.每个组件: 可选择是否将请求传递到管道中的下一个组件. 可在管道中的下一个

  • 理解ASP.NET Core 中间件(Middleware)

    目录 中间件 中间件管道 Run Use UseWhen Map MapWhen Run & Use & UseWhen & Map & Map 编写中间件并激活 基于约定的中间件 基于工厂的中间件 基于约定的中间件 VS 基于工厂的中间件 中间件 先借用微软官方文档的一张图: 可以看到,中间件实际上是一种配置在HTTP请求管道中,用来处理请求和响应的组件.它可以: 决定是否将请求传递到管道中的下一个中间件 可以在管道中的下一个中间件处理之前和之后进行操作 此外,中间件的注

  • ASP.Net Core MVC基础系列之中间件

    上一节我们介绍了服务注册和基本的管道执行流程, 并且讲到了中间件, 这一节我们就来详细谈谈中间件这个东西 讲中间件, 其实就是讲Startup类里面的ConfigureServices 和Configure 这两个方法 在程序启动类Program 中, 我们在CreateWebHostBuilder 方法中调用了UseStartup方法, 里面用泛型注入了 Startup 类, 那程序就会自动实例化这个类, 并且去执行它里面的ConfigureServices 和Configure 这两个方法.

  • ASP.NET Core自定义中间件的方式详解

    目录 1.委托形式 2.强类型中间件 2.1.定义中间件的依赖 2.2.定义中间件类型 3.基于约定的中间件 3.1.约定规则 3.2.应用实现 总结 ASP.NET Core应用本质上,其实就是由若干个中间件构建成的请求处理管道.管道相当于一个故事的框架,而中间件就相当于故事中的某些情节.同一个故事框架采用不同的情节拼凑,最终会体现出不同风格的故事.而我们的ASP.NET Core应用也正是如此,同一管道采用不同的中间件组合,最终也会呈现出不同的应用形态. 从上述的概念种可以看出,中间件在AS

  • ASP.NET Core自定义中间件如何读取Request.Body与Response.Body的内容详解

    背景# 最近在徒手造轮子,编写一个ASP.NET Core的日志监控器,其中用到了自定义中间件读取Request.Body和Response.Body的内容,但是编写过程,并不像想象中的一帆风顺,ASP.NET Core针对Request.Body和Response.Body的几个特殊设计,导致了完成以上功能需要绕一些弯路. 原始代码# 为了读取Request.Body和Response.Body的内容,我的实现思路如下: 创建一个LoggerMiddleware的中间件,将它放置在项目中间件管

  • [译]ASP.NET Core 2.0 路由引擎详解

    本文介绍了ASP.NET Core 2.0 路由引擎详解,分享给大家,具体如下: 问题 ASP.NET Core 2.0的路由引擎是如何工作的? 答案 创建一个空项目,为Startup类添加MVC服务和请求中间件: public void ConfigureServices(IServiceCollection services) { services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvir

  • ASP.NET Core Middleware的实现方法详解

    概念 ASP.NET Core Middleware是在应用程序处理管道pipeline中用于处理请求和操作响应的组件. 每个组件: 在pipeline中判断是否将请求传递给下一个组件 在处理管道的下个组件执行之前和之后执行一些工作, HttpContxt对象能跨域请求.响应的执行周期 特性和行为 ASP.NET Core处理管道由一系列请求委托组成,一环接一环的被调用, 下面给出自己绘制的Middleware pipeline流程图: 从上图可以看出,请求自进入处理管道,经历了四个中间件,每个

  • CSRF在ASP.NET Core中的处理方法详解

    前言 前几天,有个朋友问我关于AntiForgeryToken问题,由于对这一块的理解也并不深入,所以就去研究了一番,梳理了一下. 在梳理之前,还需要简单了解一下背景知识. AntiForgeryToken 可以说是处理/预防CSRF的一种处理方案. 那么什么是CSRF呢? CSRF(Cross-site request forgery)是跨站请求伪造,也被称为One Click Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用. 简单理解的话

  • ASP.NET Core中如何实现重定向详解

    前言 ASP.NET Core 是一个跨平台,开源的,轻量级的,模块化的,用于构建高性能的 web 开发框架, ASP.NET Core MVC 内置了多种方式将一个 request 请求跳转到指定的url,这篇文章我们就来讨论如何去实现. 理解 RedirectActionResult ASP.NET Core MVC 中内置了几种 Redirect,比如说:RedirectResult, RedirectToActionResult, RedirectToRouteResult 和 Loca

  • 如何使用Rotativa在ASP.NET Core MVC中创建PDF详解

    前言 在本文中,我们将学习如何使用Rotativa.AspNetCore工具从ASP.NET Core中的视图创建PDF.如果您使用ASP.NET MVC,那么Rot​​ativa工具已经可用,我们可以使用它来生成pdf. 创建一个MVC项目,无论您是core或不core,都可以nuget下包.命令如下: Install-Package Rotativa #或者 Install-Package Rotativa.AspNetCore 这个工具由意大利人Giorgio Bozio创建.他需要在AS

  • ASP.NET Core 中的Main方法详解

    在 ASP.NET Core 项目中,我们有一个名为Program.cs的文件.在这个文件中,我们有一个public static void Main()方法 . public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[]

  • ASP.NET Core缓存静态资源示例详解

    背景 缓存样式表,JavaScript或图像文件等静态资源可以提高您网站的性能.在客户端,总是从缓存中加载一个静态文件,这样可以减少对服务器的请求数量,从而减少获取页面及其资源的时间.在服务器端,由于它们的请求较少,服务器可以处理更多的客户端而无需升级硬件. 虽然缓存是一件好事,但您必须确保客户端始终运行最新版本的应用程序.当您部署下一个版本的网站时,您不希望客户端使用过时的缓存版本的文件. 方案: 为确保用户始终使用最新版本的文件,我们必须为每个文件版本提供一个唯一的URL.有很多策略: 使用

  • 在 asp.net core 的中间件中返回具体的页面的实现方法

    前言 在 asp.net core 中,存在着中间件这一概念,在中间件中,我们可以比过滤器更早的介入到 http 请求管道,从而实现对每一次的 http 请求.响应做切面处理,从而实现一些特殊的功能 在使用中间件时,我们经常实现的是鉴权.请求日志记录.全局异常处理等等这种非业务性的需求,而如果你有在 asp.net core 中使用过 swashbuckle(swagger).health check.mini profiler 等等这样的组件的话,你会发现,这些第三方的组件往往都提供了页面,允

随机推荐