6 ASP.NET Core中间件
ASP.NET Core 中间件是位于请求处理管道的一系列组件,用来处理请求和响应,在请求管道线上可以有一个或者多个中间件
1 生成响应并且发送响应到客户端
注意中间件执行的顺序按照他们在应用程序中注册的顺序执行,响应的顺序则按照相反的方向执行,假设我们有4个中间件-M1,M2,M3和M4,他们在应用程序中定义的顺序M1>M2>M3>M4,针对一个请求
1 中间件处理的顺序是M1>M2>M3>M4.
2 中间件响应返回到客户端的顺序为M4>M3>M2>M1
6.1 ASP.NET Core客户自定义中间件
在ASP.NET Core中我们可以自定义中间件,客户自定义的中间件有下列四种形式:
1 内容生成中间件(Content-Generating middleware)
2 短路中间件(Short-Circuiting middleware)
3 请求编辑中间件(Request-Editing middleware)
4 响应编辑中间件(Response-Editing middleware)
6.1.1 内容生成中间件(Content-Generating middleware)
内容生成中间件生成内容或响应,我们创建这个中间件返回响应到客户端,我们通过一个简单例子来理解如何工作
在根目录一下创建一个Moddlewares文件夹,在这个文件夹下添加一个ContextMiddleware.cs文件,代码如下:
namespace AspNetCore.Configuration.Middlewares
{
public class ContentMiddleware
{
private RequestDelegate _nextDelegate;
public ContentMiddleware(RequestDelegate next)
{
_nextDelegate = next;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Path == "/middleware")
{
await httpContext.Response.WriteAsync("这是Context 中间件");
}
else
{
_nextDelegate(httpContext);
}
}
}
}
注意中间件类没有实现任何接口或者继承任何公共基类,它有一个构造函数使用了RequestDelegate类型,构造函数的参数由MVC自动提供,RequestDelegate对象表示下一个执行的中间件
using AspNetCore.Configuration.Middlewares;
using AspNetCore.Configuration.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddSingleton<TotalUsers>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseMiddleware<ContentMiddleware>();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
中间件中使用依赖注入
using AspNetCore.Configuration.Services;
namespace AspNetCore.Configuration.Middlewares
{
public class ContentMiddleware
{
private RequestDelegate _nextDelegate;
private TotalUsers _totalUsers;
public ContentMiddleware(RequestDelegate next, TotalUsers totalUsers)
{
_nextDelegate = next;
_totalUsers = totalUsers;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Path == "/middleware")
{
await httpContext.Response.WriteAsync("this message come from ContextMiddleware" +" TotalUsers=" + _totalUsers.TUsers());
}
else
{
_nextDelegate(httpContext);
}
}
}
}
短路中间件会阻止后面中间件的执行,因为它会使整个请求管道短路,让我们创建一个短路中间件
在Middlewares文件夹下创建一个ShortCircuitMiddleware.cs类,在这个类中添加如下代码:
namespace AspNetCore.Configuration.Middlewares
{
public class ShortCircuitMiddleware
{
private RequestDelegate _next;
public ShortCircuitMiddleware(RequestDelegate requestDelegate)
{
_next = requestDelegate;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Headers["User-Agent"].Any(v => v.Contains("Firefox")))
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
else
{
await _next(context);
}
}
}
}
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
app.UseMiddleware<ShortCircuitMiddleware>();
app.UseMiddleware<ContentMiddleware>();
在别的浏览器中输入https://localhost:7034/middleware
6.1.3 请求编辑中间件(Request-Editing middleware)
namespace AspNetCore.Configuration.Middlewares
{
public class RequestEditingMiddleware
{
private RequestDelegate _next;
public RequestEditingMiddleware(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext httpContext)
{
httpContext.Items["Firefox"] = httpContext.Request.Headers["User-Agent"].Any(v => v.Contains("Firefox"));
await _next(httpContext);
}
}
}
httpContext.Items["Firefox"] = httpContext.Request.Headers["User-Agent"].Any(v => v.Contains("Firefox"));
现在我们修改一下ShortCircuitMiddleware.cs中间件,删除下面代码:
if (context.Request.Headers["User-Agent"].Any(v => v.Contains("Firefox")))
用下面代码替换
if(context.Items["Firefox"] as bool? == true)
namespace AspNetCore.Configuration.Middlewares
{
public class ShortCircuitMiddleware
{
private RequestDelegate _next;
public ShortCircuitMiddleware(RequestDelegate requestDelegate)
{
_next = requestDelegate;
}
public async Task Invoke(HttpContext context)
{
//if (context.Request.Headers["User-Agent"].Any(v => v.Contains("Firefox")))
if(context.Items["Firefox"] as bool? == true)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
else
{
await _next(context);
}
}
}
}
因此在Program.cs中注册中间件的顺序如下:
app.UseMiddleware<RequestEditingMiddleware>();
app.UseMiddleware<ShortCircuitMiddleware>();
app.UseMiddleware<ContentMiddleware>();

运行程序,firefox输入https://localhost:7034/middleware ,由于返回未授权的http因此你获取一个空白页
6.1.4 响应编辑中间件(Response-Editing middleware)
namespace AspNetCore.Configuration.Middlewares
{
public class ResponseEditingMiddleware
{
private RequestDelegate _next;
public ResponseEditingMiddleware(RequestDelegate next)
{
_next=next;
}
public async Task Invoke(HttpContext httpContext)
{
await _next(httpContext);
if (httpContext.Response.StatusCode==401)
{
await httpContext.Response.WriteAsync("Firefox browser not authorized");
}
else if(httpContext.Response.StatusCode==404)
{
await httpContext.Response.WriteAsync("No Response Generated");
}
}
}
}
在上面的代码如果HTTP响应的状态码是401,我们添加文本Firefox browser not authorized到响应中,回想一下我们在Short-Circuiting中间中生成401未授权的响应
响应编辑中间件仅仅编辑其它中间件的响应,因此它必须放到请求管道其它中间件的前面,所以注册的位置很重要
app.UseMiddleware<ResponseEditingMiddleware>();
app.UseMiddleware<RequestEditingMiddleware>();
app.UseMiddleware<ShortCircuitMiddleware>();
app.UseMiddleware<ContentMiddleware>();

现在运行你的应用程序并且在Firefox浏览器中输入URL-https://localhost:7034/middleware,你将获取到响应文本-Firefox browser not unthorized , 图片显示如下
else if(httpContext.Response.StatusCode==404)
{
await httpContext.Response.WriteAsync("No Response Generated");
}
当状态码是404时执行该代码,当请求的资源在应用程序中没有发现时该状态码会自动生成
在浏览器输入一个url-https://localhost:7034/tutorials,你会在浏览器中看到No Response Generated 消息
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
总结