ASPNetCore 中间件解析
什么是中间件
放在 ASP.NET Core 程序中,都会对应一个请求管道 (request pipeline),在这个请求管道中,我们可以动态配置各种业务逻辑对应的 中间件 (middleware) ,从而达到服务端可以针对不同用户做出不同的请求响应。在 ASP.NET Core 中,管道式编程是一个核心且基础的概念,它的很多中间件都是通过 管道式 的方式来最终配置到请求管道中的,所以理解这里面的管道式编程对我们编写更加健壮的 DotNetCore 程序相当重要 带上官网截图。
手敲源码
如何改变文本的样式
RequestDelegate
/// <summary>
/// 处理请求的委托
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public delegate Task RequestDelegate(HttpContext context);
ApplicationBuilder
/// <summary>
/// 给管道添加中间件核心类
/// </summary>
public class ApplicationBuilder
{
/// <summary>
/// 所有的中间件(我理解为处理管道)
/// </summary>
private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
/// <summary>
/// 增加中间件
/// </summary>
/// <returns></returns>
public ApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
this._components.Add(middleware);
return this;
}
/// <summary>
/// 组合中间件
/// </summary>
/// <returns></returns>
public RequestDelegate Builb()
{
//创建没有任何中间件 就返回此中间件结果
RequestDelegate requestDelegate = new RequestDelegate((conetxt) =>
{
conetxt.Response += "\n404 没有任何中间件请求\n";
return Task.CompletedTask;
});
//组合中间件 需要把中间件集合倒出来 后面讲到为什么
foreach (var item in this._components.Reverse())
{
//这是指定中间件的下一个
requestDelegate = item(requestDelegate);
}
return requestDelegate;
}
}
HttpContext
/// <summary>
/// 模拟的Http上下文
/// </summary>
public class HttpContext
{
public string Request { get; set; }
public string Response { get; set; }
public void Show()
{
Console.WriteLine($"Current Context Response : \n{Response}");
}
}
Main
static void Main(string[] args)
{
//模拟请求进入
var context = new HttpContext();
var app = new ApplicationBuilder();
{
}
//开始处理管道
app.Builb()(context);
context.Show();
}
代码解读
ApplicationBuilder
主要是 用来把所有中间件组合的核心类 其中的字段
_components 只要是全部中间件的一个集合
Use 只要是为中间件提供一共一个新增的方法 他的参数为 Func<RequestDelegate, RequestDelegate> 传入的参数和返回参数都是同一个
Builb 用来把所有的中间件组合成一个 RequestDelegate 然后把这个返回出去,等待程序调用来处理请求
RequestDelegate
上面总是用到这个委托但是这个委托到底是什么呢?
这个委托主要是为了处理 双下文的 他的参数为一个HttpContext 并且所有的都是共用一个 HttpContext
HttpContext
模拟的上下文 主要是为了好展示
Main
首先我们先使用 Use() 方法注册中间件试一下吧
app.Use((option) =>
{
//这里构建一个返回结果 RequestDelegate委托中需要参数 HttpContext * 注意现在是不会调用 middlewareDelegate(或者说自己不够理解掉坑了)
RequestDelegate middlewareDelegate = new RequestDelegate((context) =>
{
//这里开始处理请求
context.Response += "Create middleware One Start\n";
//注意这里的 option 这里可以选择是否执行下一个中间件
option(context);
context.Response += "Create middleware One End\n";
return Task.CompletedTask;
});
return middlewareDelegate;
});
注意这里的 现在是不会执行里面的
Buibl
我们 Use 之后 已经增加一个中间件 我们把它拼装一下吧
public RequestDelegate Builb()
{
//创建没有任何中间件 就返回此中间件结果
RequestDelegate requestDelegate = new RequestDelegate((conetxt) =>
{
conetxt.Response += "\n404 没有任何中间件请求\n";
return Task.CompletedTask;
});
foreach (var item in this._components)
{
requestDelegate = item(requestDelegate);
}
return requestDelegate;
}
当执行Builb 之后就会对 _components 进行拼装 把所有的委托组合成一个
开始执行之前 创建了一个默认的结果 requestDelegate 然后使用循环遍历 说白了也就是俄罗斯套娃,这里item为 Func<RequestDelegate, RequestDelegate> 他需要一个委托然后重新返回一个委托,这里执行将会把 默认的代入到第一个Use的方法当执行第二个Use里面时
等到循环结束 会把最后一个委托给返回出去 return requestDelegate;
我们来看一下执行的顺序 最后返回出去的肯定是 最后一个委托
开始执行的时候回拼装 context.Response 随后执行 option(context); 注意这里执行的 Option大家还记得是怎么拼装的吗? 它是把它的上一个委托传入进来 所有在调用的时候将会调用
但是这个里面的 option 又是 默认的那个 所有紧接着调用
等待执行完成 就到出去的时候啦 逐个拼接 End方法
最终的结果为这个样子! 会发现执行的顺序不一致 应该先执行 完全倒序了 所以 我们在 Builb() 方法中应该把 _components 倒序过来让他是从 上往下执行处理HttpContext 的
//将倒序之后 第一个处理的 是最后一个Use 返回出去的是第一个Use 从指定上一个中间件变成了指定下一个中间件
foreach (var item in this._components.Reverse())
{
requestDelegate = item(requestDelegate);
}
Reverse() 使用方法倒序集合 然后我们在执行一次
是不是这里就是自己添加的顺序了呢。但是现在出现了一个问题 默认的这个怎么去除? 这里我们已经知道了每一次调用都会有一个 option(context) 这里就是执行它的下一个委托 所以只要我们在最后一个Use的地方 不指定就好了
app.Use((option) =>
{
//这里构建一个返回结果 RequestDelegate委托中需要参数 HttpContext * 注意现在是不会调用 middlewareDelegate(或者说自己不够理解掉坑了)
RequestDelegate middlewareDelegate = new RequestDelegate((context) =>
{
//这里开始处理请求
context.Response += "End All middleware";
//不调用 option() 做到短路
return Task.CompletedTask;
});
return middlewareDelegate;
});
不调用option将可以做到短路的效果
大家肯定想每次这样写是不是觉得麻烦 现在我们把 Use方法进行进一步的扩展
/// <summary>
/// Use 方法扩展
/// </summary>
/// <param name="app"></param>
/// <param name="middleware"></param>
/// <returns></returns>
public static ApplicationBuilder Use(this ApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware)
{
return app.Use( (next) =>
delegate (HttpContext context)
{
Func<Task> arg = () => next(context);
return middleware(context, arg);
}
);
}
使用扩展方法注册