經(jīng)過前2篇文章的介紹,相信大家已經(jīng)對OWIN和Katana有了基本的了解,那么這篇文章我將繼續(xù)OWIN和Katana之旅——創(chuàng)建自定義的Middleware中間件。
何為Middleware中間件
Middleware中間件從功能上可以理解為用來處理Http請求,當(dāng)Server將Http請求封裝成符合OWIN規(guī)范的字典后,交由Middleware去處理,一般情況下,Pipeline中的Middleware以鏈?zhǔn)降男问教幚鞨ttp請求,即每一個Middleware都是最小的模塊化,彼此獨(dú)立、高效。
從語法上理解Middleware的話,他是一個應(yīng)用程序委托(Func<IDictionary<string, object>, Task>)的實(shí)例,通過使用IAppBuilder 接口的Use或者Run方法將一個Middleware插入到Pipeline中,不同的是使用Run方法不需要引用下一個Middleware,即他是Pipeline中最后的處理元素。
使用Inline方式注冊Middleware
使用Use方法可以將一個Middleware插入到Pipeline中,值得注意的是需要傳入下一個Middleware的引用,代碼如下所示:
app.Use(new Func<Func<IDictionary<string, object>, Task>/*Next*/, ?????????????Func<IDictionary<string, object>/*Environment Dictionary*/, Task>>(next => async env => ?????????????{ ?????????????????string before = "Middleware1--Before(inline)"+Environment.NewLine; ?????????????????string after = "Middleware1--After(inline)"+Environment.NewLine; ?????????????????var response = env["owin.ResponseBody"] as Stream; ?????????????????await response.WriteAsync(Encoding.UTF8.GetBytes(before), 0, before.Length); ?????????????????await next.Invoke(env); ?????????????????await response.WriteAsync(Encoding.UTF8.GetBytes(after), 0, after.Length); ?????????})); 上述代碼中,實(shí)例化了一個委托,它需要傳入下一個Pipeline中的Middleware引用同時返回一個新的Middleware并插入到Pipeline中。因?yàn)槭钱惒降?#xff0c;所以別忘了async、await關(guān)鍵字。
使用Inline+ AppFunc方式注冊Middleware
為了簡化書寫,我為應(yīng)用程序委托(Func<IDictionary<string, object>, Task>)類型創(chuàng)建了別名AppFunc:
using AppFunc=Func<IDictionary<string,object>/*Environment Dictionary*/,Task/*Task*/>; 所以又可以使用如下方式來講Middleware添加到Pipeline中:
app.Use(new Func<AppFunc, AppFunc>(next => async env => { ????string before = "\tMiddleware2--Before(inline+AppFunc)" + Environment.NewLine; ????string after = "\tMiddleware2--After(inline+AppFunc)" + Environment.NewLine; ????var response = env["owin.ResponseBody"] as Stream; ????await response.WriteAsync(Encoding.UTF8.GetBytes(before), 0, before.Length); ????await next.Invoke(env); ????await response.WriteAsync(Encoding.UTF8.GetBytes(after), 0, after.Length); })); 考慮到業(yè)務(wù)邏輯的增長,有必要將Lambda表達(dá)式中的處理邏輯給分離開來,所以對上述代碼稍作修改,提取到一個名為Invoke的方法內(nèi):
app.Use(new Func<AppFunc, AppFunc>(next => env => Invoke(next, env))); private async Task Invoke(Func<IDictionary<string, object>, Task> next, IDictionary<string, object> env) ????????{ ????????????var response = env["owin.ResponseBody"] as Stream; ????????????string pre = "\t\tMiddleware 3 - Before (inline+AppFunc+Invoke)" + Environment.NewLine; ????????????string post = "\t\tMiddleware 3 - After (inline+AppFunc+Invoke)" + Environment.NewLine; ????????????await response.WriteAsync(Encoding.UTF8.GetBytes(pre), 0, pre.Length); ????????????await next.Invoke(env); ????????????await response.WriteAsync(Encoding.UTF8.GetBytes(post), 0, post.Length); ????????} 雖然將業(yè)務(wù)邏輯抽取到一個方法中,但I(xiàn)nline這種模式對于復(fù)雜的Middleware還是顯得不夠簡潔、易懂。我們更傾向于創(chuàng)建一個單獨(dú)的類來表示。
定義原生Middleware類的形式來注冊Middleware
如果你只想簡單的跟蹤一下請求,使用Inline也是可行的,但對于復(fù)雜的Middleware,我傾向于創(chuàng)建一個單獨(dú)的類,如下所示:
public class RawMiddleware ??{ ??????private readonly AppFunc _next; ??????public RawMiddleware(AppFunc next) ??????{ ??????????this._next = next; ??????} ??????public async Task Invoke(IDictionary<string,object> env ) ??????{ ??????????var response = env["owin.ResponseBody"] as Stream; ??????????string pre = "\t\t\tMiddleware 4 - Before (RawMiddleware)" + Environment.NewLine; ??????????string post = "\t\t\tMiddleware 4 - After (RawMiddleware)\r\n" + Environment.NewLine; ??????????await response.WriteAsync(Encoding.UTF8.GetBytes(pre), 0, pre.Length); ??????????await _next.Invoke(env); ??????????await response.WriteAsync(Encoding.UTF8.GetBytes(post), 0, post.Length); ??????} ??} 最后,依舊是通過Use方法來將Middleware添加到Pipeline中:
//兩者方式皆可 //app.Use<RawMiddleware>(); app.Use(typeof (RawMiddleware)); 上述代碼中,IAppBuilder實(shí)例的Use方法添加Middleware至Pipeline與Inline方式有很大不同,它接受一個Type而非Lambda表達(dá)式。在這種情形下,創(chuàng)建了一個Middleware類型的實(shí)例,并將Pipeline中下一個Middleware傳遞到構(gòu)造函數(shù)中,最后當(dāng)Middleware被執(zhí)行時調(diào)用Invoke方法。
注意Middleware是基于約定的形式定義的,需要滿足如下條件:
- 構(gòu)造函數(shù)的第一個參數(shù)必須是Pipeline中下一個Middleware
- 必須包含一個Invoke方法,它接收Owin環(huán)境字典,并返回Task
使用Katana Helper來注冊Middleware
程序集Microsoft.Owin包含了Katana為我們提供的Helper,通過他,可以簡化我們的開發(fā),比如IOwinContext封裝了Owin的環(huán)境字典,強(qiáng)類型對象可以通過屬性的形式獲取相關(guān)數(shù)據(jù),同時為IAppBuilder提供了豐富的擴(kuò)展方法來簡化Middleware的注冊,如下所示:
app.Use(async (context, next) => ???????????{ ???????????????await context.Response.WriteAsync("\t\t\t\tMiddleware 5--Befone(inline+katana helper)"+Environment.NewLine); ???????????????await next(); ???????????????await context.Response.WriteAsync("\t\t\t\tMiddleware 5--After(inline+katana helper)"+Environment.NewLine); ???????????}); 當(dāng)然我們也可以定義一個Middleware類并繼承OwinMiddleware,如下所示:
public class MyMiddleware : OwinMiddleware ???{ ???????public MyMiddleware(OwinMiddleware next) ???????????: base(next) ???????{ ? ???????} ???????public override async Task Invoke(IOwinContext context) ???????{ ???????????await context.Response.WriteAsync("\t\t\t\t\tMiddleware 6 - Before (Katana helped middleware class)"+Environment.NewLine); ???????????await this.Next.Invoke(context); ???????????await context.Response.WriteAsync("\t\t\t\t\tMiddleware 6 - After (Katana helped middleware class)"+Environment.NewLine); ???????} ???} 然后將其添加到Pipeline中:
app.Use<MyMiddleware>(); Middleware的執(zhí)行順序
在完成上面Middleware注冊之后,在Configuration方法的最后添加最后一個的Middleware中間件,注意它并不需要對下一個Middleware的引用了,我們可以使用Run方法來完成注冊:
app.Run(context => context.Response.WriteAsync("\t\t\t\t\t\tHello World"+Environment.NewLine)); 值得注意的是,Pipeline中Middleware處理Http Request順序同注冊順序保持一致,即和Configuration方法中書寫的順序保持一致,Response順序則正好相反,如下圖所示:
最后,運(yùn)行程序,查看具體的輸出結(jié)果是否和我們分析的保持一致:
小結(jié)
在這篇文章中,我為大家講解了自定義Middleware的創(chuàng)建,Katana為我們提供了非常多的方式來創(chuàng)建和注冊Middleware,在下一篇文章中,我將繼續(xù)OWIN和Katana之旅,探索Katana和其他Web Framework的集成。
轉(zhuǎn)載于:https://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-create-custom-middleware.html
總結(jié)
以上是生活随笔為你收集整理的ASP.NET MVC随想录——创建自定义的Middleware中间件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。