ASP.NET 6 中间件系列 - 执行顺序

80b73b8994a1510e000abad6ac297281.png

这篇文章是 ASP.NET 6 中间件系列文章的第 3 部分,你还可以阅读第1部分第2部分

我们通过中间件创建的管道是有执行顺序的,执行顺序与中间件的添加顺序是相同的,接下来我们讨论一下为什么要有执行顺序,以及它的重要性。

示例项目

在 GitHub 上可以获得这篇文章涉及到的代码:

https://github.com/zilor-net/ASPNET6Middleware/tree/Part3

执行顺序

在本系列的第1部分中,中间件构成了一个管道,该管道中的中间件按照一定的顺序执行,如下图所示:

f5421de9e3f981b4e2b1733e8fa8326f.png

请求按顺序经过各个中间件,而响应则按相反的顺序返回。

在前面的文章中,我们已经定义了两个中间件类:

  • LoggingMiddleware用于记录请求/响应日志;

  • SimpleResponseMiddleware用于中断管道,返回响应。

在这篇文章中,我们仍然以LoggingMiddleware为例:

app.UseLoggingMiddleware();

添加延迟

我们创建一个新的中间件类,叫做IntentionalDelayMiddleware,它看起来像这样:

namespace  MiddlewareNET6Demo.Middleware
{
    public  class  IntentionalDelayMiddleware
    {
        private  readonly RequestDelegate _next;

        public IntentionalDelayMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            await Task.Delay(100);

            await _next(context);

            await Task.Delay(100);
        }
    }
}

这个中间件在处理传入请求和处理传出响应时都会等待 100ms,总等待时间为 200ms。

当然,实际场景下,我们并不会这么做。

在这里,IntentionalDelayMiddleware只是代表了某种未定义的中间件,它需要一个可预测的时间来执行。

我们需要向管道中添加一个IntentionalDelayMiddleware的实例。问题在于,我们是在LoggingMiddleware之前还是之后添加它?

其实在这种情况下,这个问题可能并不重要,因为这两个中间件不会进行交互,也不处理相同的事情。

在这个示例中,让我们在LoggingMiddleware之后添加IntentionalDelayMiddleware

app.UseLoggingMiddleware();
app.UseIntentionalDelayMiddleware();

如果现在运行应用程序,我们可能不会发现明显的差异,因为 200 毫秒相当快。

添加执行时间中间件

为了监视每个请求的所消耗的时间,我们往往需要记录每个请求到我们系统的执行时间。

这个需求对于中间件来说是非常简单的,我们可以使用 .NET 提供的Stopwatch类和第2篇文章中创建的LoggingService来实现。

下面是名为TimeLoggingMiddleware的中间件类:

using MiddlewareNET6Demo.Logging;
using System.Diagnostics;

namespace  MiddlewareNET6Demo.Middleware
{
    public  class  TimeLoggingMiddleware
    {
        private  readonly RequestDelegate _next;
        private  readonly ILoggingService _logger;

        public TimeLoggingMiddleware(RequestDelegate next, 
                                     ILoggingService logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();

            await _next(context);

            watch.Stop();
            _logger.Log(LogLevel.Information, "Time to execute: " + watch.ElapsedMilliseconds + " milliseconds.");
        }
    }
}

我们需要将其添加到管道中。但是,这里仍然有个问题:我们应该添加到哪个位置?

如果我们将TimeLoggingMiddleware添加到IntentionalDelayMiddleware之前,那么后者所引起的延迟将包含在前者所度量的范围中。

如果我们将TimeLoggingMiddleware添加到IntentionalDelayMiddleware之后,那么后者所引起的延迟将不会包含在前者所度量的范围中。

让我们来看看管道:

app.UseHttpsRedirection();
app.UseStaticFiles();

// 如果该中间件发生任何延迟,那么该延迟不会包含在时间日志中。
app.UseLoggingMiddleware();

// 时间记录中间件
app.UseTimeLoggingMiddleware();

// 延迟中间件。
// 此时,延迟被包含在时间日志中。
app.UseIntentionalDelayMiddleware();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

在这个 Program.cs 文件中,哪个位置更适合放置TimeLoggingMiddleware?其答案取决于几个问题:

  • 时间日志需要包括诸如无效授权之类的执行时间吗?如果是,那么必须在调用app.UseAuthorization()之前,放置TimeLoggingMiddleware

  • 路由调用需要的时间非常少,但可以测量。我们要把它包括进去吗?如果是,就必须在调用app.UseRouting()之前,放置TimeLoggingMiddleware

像大多数现实世界的问题一样,这个问题没有明确的答案。

如果没有明确的指示,那么这最终需要由开发人员根据系统的具体情况来做出决定。

需要注意的是:

app.UseIntentionalDelayMiddleware();
app.UseTimeLoggingMiddleware();

这两个是完全不同的:

app.UseTimeLoggingMiddleware();
app.UseIntentionalDelayMiddleware();

这就是为什么中间件在管道中顺序很重要的一个例子。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值