Asp.Net Core中间件学习
起因
从Asp.Net(Webform/Mvc/Api)到Asp.Net Core不同就是请求管道换成中间件,在Asp.Net WebForm到MVC请求管道(底层)变化不是很大.看Asp.Net管道可以参考这篇很早之前在博客园写的博客:重新认识Asp.Net管道模型中间件是一种装配到应用管道以处理请求和响应的软件
作用:- 选择是否将请求传递到管道中的下一个组件
- 可在管道中的下一个组件前后执行工作

第一个中间件
在Asp.Net Core Web程序中,在Startup类Configure方法增加以下代码:app.Use(async (context, next) =>
{
//context为HttpContext上下文包含HttpRequest(请求信息)和HttpResponse(响应信息)
//context.Request为HttpRequest
//context.Response为和HttpResponse
await context.Response.WriteAsync("hello kestrel,hello asp.net core \n");
});
app.Use(next =>
{
Console.WriteLine("1");
return async c =>
{
//_logger.LogInformation("===============/========");
await c.Response.WriteAsync("kestrel exec middleware start 1 \n");
await next.Invoke(c);
await c.Response.WriteAsync("kestrel exec middleware end 1 \n");
};
});
app.Use(next =>
{
Console.WriteLine("2");
return async c =>
{
//_logger.LogInformation("===============/========");
await c.Response.WriteAsync("kestrel exec middleware start 2 \n");
await next.Invoke(c);
await c.Response.WriteAsync("kestrel exec middleware end 2 \n");
};
});
app.Use(next =>
{
Console.WriteLine("3");
return async c =>
{
//_logger.LogInformation("===============/========");
await c.Response.WriteAsync("kestrel exec middleware start 3 \n");
await c.Response.WriteAsync("kestrel exec middleware end 3 \n");
};
});
查看Kestrel启动后的打印顺序:
在浏览器中访问: http://localhost:5000
控制台的顺序和在浏览器的结果,是相反的.为什么是相反的? 带着好奇心我们去看看源码是什么样的.
github上ApplicationBuilder地址为:https://github.com/dotnet/aspnetcore/blob/v3.1.0/src/Http/Http/src/Builder/ApplicationBuilder.cs
/// <summary>
/// 存放HttpContext 委托集合
/// </summary>
private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
/// <summary>
/// Use函数只是把中间件(委托)放到集合
/// </summary>
/// <param name="middleware"></param>
/// <returns></returns>
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
_components.Add(middleware);
return this;
}
public IApplicationBuilder New()
{
return new ApplicationBuilder(this);
}
/// <summary>
/// Build函数才是执行中间件的
/// </summary>
/// <returns></returns>
public RequestDelegate Build()
{
RequestDelegate app = context =>
{
// If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
// This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
var endpoint = context.GetEndpoint();
var endpointRequestDelegate = endpoint?.RequestDelegate;
if (endpointRequestDelegate != null)
{
var message =
$"The request reached the end of the pipeline without executing the endpoint: '{endpoint!.DisplayName}'. " +
$"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " +
$"routing.";
throw new InvalidOperationException(message);
}
context.Response.StatusCode = StatusCodes.Status404NotFound;
return Task.CompletedTask;
};
//_components调用Reverse 进行反转 所以在启动的时候先打印最后一个中间件
//那为什么在请求的时候,中间件的执行又按照我们添加中间件的顺序呢?
//是因为_components在循环结束,app指向的是第一个中间件
//在第一个中间件中输出kestrel exec middleware start 1 调用 next(调用第二个) 形成链式调用
//所以在结尾的中间件中不能进行next
foreach (var component in _components.Reverse())
{
//原本是component(app) 后跟Invoke看起来更清晰
app = component(app).Invoke();
}
return app;
}
秋风
2020-07-25