0
点赞
收藏
分享

微信扫一扫

ASP.NET Core3.1 中间件middleware讲解

 

中间件是一种装配到应用管道以处理请求和响应的软件。 每个组件:

  • 选择是否将请求传递到管道中的下一个组件。
  • 可在管道中的下一个组件前后执行工作。

请求委托用于生成请求管道。 请求委托处理每个 HTTP 请求。

使用 RunMap 和 Use 扩展方法来配置请求委托。 可将一个单独的请求委托并行指定为匿名方法(称为并行中间件),或在可重用的类中对其进行定义。 这些可重用的类和并行匿名方法即为中间件 ,也叫中间件组件 。 请求管道中的每个中间件组件负责调用管道中的下一个组件,或使管道短路。 当中间件短路时,它被称为“终端中间件” ,因为它阻止中间件进一步处理请求。

将 HTTP 处理程序和模块迁移到 ASP.NET Core 中间件介绍了 ASP.NET Core 和 ASP.NET 4.x 中请求管道之间的差异,并提供了更多的中间件示例。

使用 IApplicationBuilder 创建中间件管道

ASP.NET Core 请求管道包含一系列请求委托,依次调用。 下图演示了这一概念。 沿黑色箭头执行。

ASP.NET Core3.1 中间件middleware讲解_匿名方法

每个委托均可在下一个委托前后执行操作。 应尽早在管道中调用异常处理委托,这样它们就能捕获在管道的后期阶段发生的异常。

尽可能简单的 ASP.NET Core 应用设置了处理所有请求的单个请求委托。 这种情况不包括实际请求管道。 调用单个匿名函数以响应每个 HTTP 请求。

public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});
}
}

用 Use 将多个请求委托链接在一起。 ​​next​​ 参数表示管道中的下一个委托。 可通过不 调用 next 参数使管道短路。 通常可在下一个委托前后执行操作,如以下示例所示:

public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do work that doesn't 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.");
});
}
}

当委托不将请求传递给下一个委托时,它被称为“让请求管道短路” 。 通常需要短路,因为这样可以避免不必要的工作。 例如,静态文件中间件可以处理对静态文件的请求,并让管道的其余部分短路,从而起到终端中间件 的作用。 如果中间件添加到管道中,且位于终止进一步处理的中间件前,它们仍处理 ​​next.Invoke​​ 语句后面的代码。 不过,请参阅下面有关尝试对已发送的响应执行写入操作的警告。

警告

在向客户端发送响应后,请勿调用 ​​next.Invoke​​​。 响应启动后,针对 HttpResponse 的更改将引发异常。 例如,设置标头和状态代码更改将引发异常。 调用 ​​next​​ 后写入响应正文:

  • 可能导致违反协议。 例如,写入的长度超过规定的​​Content-Length​​。
  • 可能损坏正文格式。 例如,向 CSS 文件中写入 HTML 页脚。

HasStarted 是一个有用的提示,指示是否已发送标头或已写入正文。

 

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

public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do work that doesn't 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.");
});
}
}

在前面的示例中,​​Run​​​ 委托将 ​​"Hello from 2nd delegate."​​​ 写入响应,然后终止管道。 如果在 ​​Run​​​ 委托之后添加了另一个 ​​Use​​​ 或 ​​Run​​ 委托,则不会调用该委托。

中间件顺序

向 ​​Startup.Configure​​ 方法添加中间件组件的顺序定义了针对请求调用这些组件的顺序,以及响应的相反顺序。 此顺序对于安全性、性能和功能至关重要。

下面的 ​​Startup.Configure​​ 方法按照建议的顺序增加与安全相关的中间件组件:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();

app.UseRouting();
// app.UseRequestLocalization();
// app.UseCors();

app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();

app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllerRoute(
name: "default",
pattern: "{cnotallow=Home}/{actinotallow=Index}/{id?}");
});
}

对中间件管道进行分支

Map 扩展用作约定来创建管道分支。 ​​Map​​ 基于给定请求路径的匹配项来创建请求管道分支。 如果请求路径以给定路径开头,则执行分支。

 

public class Startup
{
private static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}

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

public void Configure(IApplicationBuilder app)
{
app.Map("/map1", HandleMapTest1);

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

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

 

参考网址:

​​https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.builder?view=aspnetcore-3.1​​

 作者:沐雪

文章均系作者原创或翻译,如有错误不妥之处,欢迎各位批评指正。本文版权归作者所有,如需转载恳请注明。
​​​ 为之网-热爱软件编程 http://www.weizhi.cc/​​

举报

相关推荐

0 条评论