.NetCore中间件实现原理
中间件介绍
中间件是在应用程序管道处理请求和响应的一个链
每个组件都可以在请求处理前后做一些操作,并决定是否将请求交给下一个组件处理
如果一个中间件没有把请求交给下一个中间件,称之为管道短路
中间件的默认实现类在 Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder 中
中间件配置
配置中间件的方式很多,包括UseMiddleware,Use,Run等等
但大部分配置方式都是扩展方法,最终调用的函数只有 Use(Func<RequestDelegate, RequestDelegate> middleware)
核心代码
public delegate Task RequestDelegate(HttpContext context);
private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
_components.Add(middleware);
return this;
}
public RequestDelegate Build()
{
RequestDelegate requestDelegate = delegate(HttpContext context)
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
};
foreach (Func<RequestDelegate, RequestDelegate> item in _components.Reverse())
{
requestDelegate = item(requestDelegate);
}
return requestDelegate;
}
这是ApplicationBuilder中的核心代码,一眼看上去很简单。但是这么这么多层的委托嵌套难以阅读,我们接下来将他们拆开来看
RequestDelegate
这个委托不用多说,参数为HttpContext上下文,返回一个Task
Func<RequestDelegate, RequestDelegate>
这个就有意思了,他的入参是Next,返回值是当前要执行的委托,不要理解反了。
我们在Startup的Configure中写入以下代码
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use((RequestDelegate next) => async (HttpContext context) =>
{
Console.WriteLine("before1");
await next.Invoke(context);
Console.WriteLine("after1");
});
app.Use((RequestDelegate next) => async (HttpContext context) =>
{
Console.WriteLine("before2");
await next.Invoke(context);
Console.WriteLine("after2");
});
app.Use((RequestDelegate next) => async (HttpContext context) =>
{
if (context.Request.Path == "/hello")
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("hello");
return;
}
await next.Invoke(context);
});
}
执行结果如下
before1
before2
hello
after2
after1
如果仍然难以看懂,那我们继续拆分
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Func<RequestDelegate, RequestDelegate> func1 = (RequestDelegate next) =>
{
return async context =>
{
{
Console.WriteLine("before1");
await next.Invoke(context);
Console.WriteLine("after1");
}
};
};
Func<RequestDelegate, RequestDelegate> func2 = (RequestDelegate next) =>
{
return async context =>
{
{
Console.WriteLine("before2");
await next.Invoke(context);
Console.WriteLine("after2");
}
};
};
Func<RequestDelegate, RequestDelegate> func3 = (RequestDelegate next) =>
{
return async context =>
{
{
if (context.Request.Path == "/hello")
{
Console.WriteLine("hello");
context.Response.StatusCode = 200;
await context.Response.WriteAsync("hello");
return;
}
await next.Invoke(context);
}
};
};
app.Use(func1);
app.Use(func2);
app.Use(func3);
}
接下来我们看一下中间件的构建过程,来解释为什么会是上面的输出结果
//Use()函数负责向_components列表中插入委托,此时_components中存在的委托顺序为 func1,func2,func3
//接下来我们主要看Build()函数
//1.这是build函数中定义的最后一个短路委托,不会再向下调用
RequestDelegate requestDelegate = delegate(HttpContext context)
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
};
//2._components.Reverse()将注入的委托顺序反转,执行循环
// 反转后的顺序变成了 func3 ,func2, func1
// 第一次1循环得到结果: requestDelegate=func3(requestDelegate)
// 第一次2循环得到结果: requestDelegate=func2(func3(requestDelegate))
// 第一次3循环得到结果: requestDelegate=func1(func2(func3(requestDelegate)))
//3. 结合我们的代码,最终执行顺序如下
requestDelegate = async (context) =>
{
Console.WriteLine("before1");
Console.WriteLine("before2");
if (context.Request.Path == "/hello")
{
Console.WriteLine("hello");
context.Response.StatusCode = 200;
await context.Response.WriteAsync("hello");
return;
}
context.Response.StatusCode = 404;
Console.WriteLine("after2");
Console.WriteLine("after1");
};
//4.最终将requestDelegate返回
所以在接收到请求时,中间件的处理顺序就会按照我们定义的顺序来执行啦,经过上面的介绍,是不是感觉So Easy呐!