中間件介紹
中間件是在應用程序管道處理請求和響應的一個鏈
每個組件都可以在請求處理前后做一些操作,並決定是否將請求交給下一個組件處理
如果一個中間件沒有把請求交給下一個中間件,稱之為管道短路
中間件的默認實現類在 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吶!