.Net Core中的管道机制

1、名词解释

中间件

中间件是一种装配到应用管道以处理请求和响应的软件,选择是否将请求传递到管道中的下一个组件,可在管道中的下一个组件前后执行工作。

请求管道

请求处理管道由一个Server和一组有序排列的中间件构成。Server负责监听、接收请求和响应,而中间件则负责处理请求和响应之间的所有工作。

请求委托

请求委托(RequestDelegate)是ASP.NET Core中用于处理HTTP请求的一个委托类型。‌ 它是一个函数,代表一项请求处理任务,接受一个HttpContext对象作为参数,并返回一个Task。每个中间件都承载着独立的请求处理任务,通过RequestDelegate来表示,使得每个中间件可以执行特定的操作,并在必要时将请求传递给管道中的下一个中间件。

管道短路

当委托不将请求传递给下一个委托时,它被称为“让请求管道短路”。通常需要短路,因为这样可以避免不必要的工作。

终端中间件

如果中间件让请求处理管道短路,并阻止下游中间件进一步处理请求,它被称为“终端中间件”。

Endpoint

在中间件中,Endpoint是指客户端和服务器之间通信的一个具体点,通常表示一个特定的URL路径和HTTP方法组合。‌ Endpoint是路由系统中的一个基本单位,用于处理客户端的请求。每个Endpoint都有一个唯一的标识符,通常是一个URL路径和HTTP方法(如GET、POST等)的组合‌。

2、创建管道

使用 WebApplication 创建中间件管道:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

//请求委托用于生成请求管道。 请求委托处理每个 HTTP 请求。
app.Use(async (context, next) =>
{
    // Do work that can write to the Response.
    await next.Invoke();
    // Do logging or other work that doesn't write to the Response.
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from 2nd delegate.");
});

app.Run();

请求管道包含一系列请求委托,依次调用,每个委托处理之后又依次反向返回,如下流程图:
在这里插入图片描述

添加中间件的方法

  • Use()
  • Run()
  • Map()
  • UseWhen()
  • MapWhen()

可以看看下图Use()方法的参数,为什么是委托?为什么在使用Use()方法时需要加async关键字
在这里插入图片描述
在这里插入图片描述

Run委托

Run 委托不会收到 next 参数。 第一个 Run 委托始终为终端,用于终止管道。 Run 是一种约定。 某些中间件组件可能会公开在管道末尾运行的 Run[Middleware] 方法:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Use(async (context, next) =>
{
    // Do work that can write to the Response.
    await next.Invoke();
    // Do logging or other work that doesn't write to the Response.
});

//顺序第一个run委托终止管道,终端中间件
app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from 2nd delegate.");
});

//如果在 Run 委托之后添加了另一个 Use 或 Run 委托,则不会调用该委托。

app.Run();

中间件顺序

下图显示了 ASP.NET Core MVC 和 Razor Pages 应用的完整请求处理管道。 你可以在典型应用中了解现有中间件的顺序,以及在哪里添加自定义中间件。 你可以完全控制如何重新排列现有中间件,或根据场景需要注入新的自定义中间件。
在这里插入图片描述

常用场景中间件

以下 Program.cs 代码将为常见应用场景添加中间件组件:

  1. 异常/错误处理
  • 当应用在开发环境中运行时:
    • 开发人员异常页中间件 (UseDeveloperExceptionPage) 报告应用运行时错误。
    • 数据库错误页中间件 (UseDatabaseErrorPage) 报告数据库运行时错误。
  • 当应用在生产环境中运行时:
    • 异常处理程序中间件 (UseExceptionHandler) 捕获以下中间件中引发的异常。
    • HTTP 严格传输安全协议 (HSTS) 中间件 (UseHsts) 添加 Strict-Transport-Security 标头。
  1. HTTPS 重定向中间件 (UseHttpsRedirection) 将 HTTP 请求重定向到 HTTPS。
  2. 静态文件中间件 (UseStaticFiles) 返回静态文件,并简化进一步请求处理。
  3. Cookie 策略中间件 (UseCookiePolicy) 使应用符合欧盟一般数据保护条例 (GDPR) 规定。
  4. 用于路由请求的路由中间件 (UseRouting)。
  5. 身份验证中间件 (UseAuthentication) 尝试对用户进行身份验证,然后才会允许用户访问安全资源。
  6. 用于授权用户访问安全资源的授权中间件 (UseAuthorization)。
  7. 会话中间件 (UseSession) 建立和维护会话状态。 如果应用使用会话状态,请在 Cookie 策略中间件之后和 MVC 中间件之前调用会话中间件。
  8. 用于将 Razor Pages 终结点添加到请求管道的终结点路由中间件(带有 MapRazorPages 的 UseEndpoints)。

3、对中间件管道进行分支

Map()

Map 扩展用作约定来创建管道分支。 Map 基于给定请求路径的匹配项来创建请求管道分支。 如果请求路径以给定路径开头,则执行分支。
使用 Map 时,将从 HttpRequest.Path 中删除匹配的路径段,并针对每个请求将该路径段追加到 HttpRequest.PathBase。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

//map创建新管道分支
app.Map("/map1", HandleMapTest1);

app.Map("/map2", HandleMapTest2);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from non-Map delegate.");
});

app.Run();

//中间件,委托方法
static void HandleMapTest1(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map Test 1");
    });
}

static void HandleMapTest2(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map Test 2");
    });
}

上述代码的请求路径匹配执行记录,可明显区分管道分支,如下:
在这里插入图片描述
Map支持嵌套:

//嵌套类似于同时匹配多个段
app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map支持同时匹配多个段:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

//嵌套类似于同时匹配多个段
app.Map("/map1/seg1", HandleMultiSeg);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from non-Map delegate.");
});

app.Run();

static void HandleMultiSeg(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map Test 1");
    });
}

MapWhen()

MapWhen 基于给定谓词的结果创建请求管道分支。 Func<HttpContext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

//contains谓词用于检测查询字符串变量 branch 是否存在
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from non-Map delegate.");
});

app.Run();

static void HandleBranch(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        var branchVer = context.Request.Query["branch"];
        await context.Response.WriteAsync($"Branch used = {branchVer}");
    });
}

在这里插入图片描述

UseWhen()

UseWhen 也基于给定谓词的结果创建请求管道分支。 与 MapWhen 不同的是,如果这个分支不发生短路或包含终端中间件,则会重新加入主管道:
在下面的示例中,为所有请求写入 Hello from non-Map delegate. 响应。 如果请求中包含查询字符串变量 branch,则在重新加入主管道之前会记录其值。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
    appBuilder => HandleBranchAndRejoin(appBuilder));

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from non-Map delegate.");
});

app.Run();

void HandleBranchAndRejoin(IApplicationBuilder app)
{
    var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>(); 

    app.Use(async (context, next) =>
    {
        var branchVer = context.Request.Query["branch"];
        logger.LogInformation("Branch used = {branchVer}", branchVer);

        // Do work that doesn't write to the Response.
        await next();
        // Do other work that doesn't write to the Response.
    });
}

结尾

知识点介绍稍微粗略,主要的知识都可以通过微软官方文档进行学习,后续如我有更深入的理解会继续完善文章,学习知识点希望先了解是什么?用到知识点之后,一方面是加深知识点记忆,另一方面是有更深入的理解之后再来写为什么?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值