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接口或继承某些基类,它只用按照框架约定的方式定义中间件类型即可,具体的约定规则如下:

    中间件类型必须要定义为一个公共的、可供外界实例化的类型,静态类型无效;构造函数的参数中必须包含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自定义中间件内容请搜索真格学网以前的文章或继续浏览下面的相关文章希望大家以后多多支持真格学网!

您可能感兴趣的文章:Asp.Net?Core7?preview4限流中间件新特性详解ASP.NET?Core使用自定义日志中间件ASP.NET Core中间件ASP.Net Core MVC基础系列之中间件ASP.NET Core基础之中间件理解ASP.NET Core 中间件(Middleware)

  • 本文相关:
  • 在asp.net里得到网站的域名
  • asp.net保持页面滚动条位置(页面提交后不变)
  • .net 应对网站访问压力的方案总结
  • .net core 下使用zkweb.system.drawing实现验证码功能(图形验证码)
  • 解析abp框架中的数据传输对象与应用服务
  • c#中的cookie编程简单实例与说明
  • asp.net core razor自定义taghelper的方法
  • 后缀为 ashx 与 axd 的文件区别浅析
  • .net微信小程序用户数据的签名验证和解密代码
  • .netcore使用swagger+api多版本控制的流程分析
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全基础应用实用技巧自学过程首页asp.net实用技巧asp.net?core7?preview4限流中间件新特性详解asp.net?core使用自定义日志中间件asp.net core中间件asp.net core mvc基础系列之中间件asp.net core基础之中间件理解asp.net core 中间件(middleware)在asp.net里得到网站的域名asp.net保持页面滚动条位置(页面提交后不变).net 应对网站访问压力的方案总结.net core 下使用zkweb.system.drawing实现验证码功能(图形验证码)解析abp框架中的数据传输对象与应用服务c#中的cookie编程简单实例与说明asp.net core razor自定义taghelper的方法后缀为 ashx 与 axd 的文件区别浅析.net微信小程序用户数据的签名验证和解密代码.netcore使用swagger+api多版本控制的流程分析未将对象引用设置到对象的实例 (system.nullrefjava正则表达式 pattern和matcherasp.net(c#)网页跳转七种方法小结未能加载文件或程序集“xxx”或它的某一个依赖项。试图加载格asp.net中的几种弹出框提示基本实现方法asp.net“服务器应用程序不可用” 解决方法asp.net gridview 72般绝技asp.net生成excel并导出下载五种实现方法system.runtime.interopservicesasp.net对路径"xxxxx"的访问spring mvc整合freemarker基于注解方式visual studio 2017正式版离线安装教程.net实现延迟队列asp.net outputcache详解asp.net中javascript的引用(直接引入和间接引入)在asp.net web forms中使用依赖注入的步骤coolite优化导出excel文件实现代码asp.net设置404页面返回302http状态码的解决方法子窗口给父窗口赋值实现思路及案例演示详解asp.net core和asp.net framework共享身份验证
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved