[Asp.net 5] ApplicationBuilder詳解

來源:互聯網
上載者:User

標籤:

ApplicationBuilder(IApplicationBuilder介面),是OWIN的基礎,而且裡面都是代理、代理的代理,各種lambda運算式,估計要看這部分代碼,很多人得頭昏腦漲。今天就對個類以及幾個擴充方法進行講解。

按慣例先貼代碼(這是我修改後的,將介面繼承去掉了、HttpContext類修改成自己的MyHttpContext類)

public class ApplicationBuilder    {        private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();        public ApplicationBuilder() { }        private ApplicationBuilder(ApplicationBuilder builder)        {        }        public ApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)        {            _components.Add(middleware);            return this;        }        public ApplicationBuilder New()        {            return new ApplicationBuilder(this);        }        public RequestDelegate Build()        {            RequestDelegate app = context =>            {                context.StatusCode = "404";                System.Console.WriteLine("404");                return Task.FromResult(0);            };            foreach (var component in _components.Reverse())            {                app = component(app);            }            return app;        }    }
View Code

 RequestDelegate的定義如下:

public delegate Task RequestDelegate(MyHttpContext context);

從ApplicationBuilder的原始碼中我們可以關注3個點:_components、Use方法、Build方法。

  • _components是也一個列表(IList)對象,不過裡面類型有點特殊——是以代理RequestDelegate為參數、代理RequestDelegate為傳回值的一個代理。這裡用代理說有點別嘴,可以把代理叫做函數,就是裡面的類型是一個函數,這個函數的參數也是函數,傳回值也是函數。
  • Use方法,就是在上面的列表對象後面添加一條新記錄。
  • Build方法就是將_components數組按照反向順序,製作成一個鏈式結構(有點類似鏈表的感覺)。下面用倆幅圖說明下:

  Build之前

 

  Build之後

  我們還可以從代碼中看到Item1的參數給的是“404”,而返回結果是RequestDelegate類型。也就是說這個返回類似於void RequestDelegate(MyHttpContext context)。如果系統給我們一個context變數,那麼這個管道就可以從頭到尾的跑下去了。而事實上在Asp.net5中,這個管道就是用於替代傳統的IHttpModule的(可能不準確),那現在問題就來了,Item1的參數是這個管道的第一環還是最後一環呢?從圖形來看應該是第一環,但是事實上這是一個誤解。因為箭頭兩面一個是參數,一個是執行體(參數是一個方法,會在執行體內調用執行)。在執行體內,可能在開始就執行參數的內容,之後執行具體的內容;也可以是先執行具體內容,之後執行參數,最後在執行一部分具體內容;還可以先執行具體內容,之後參數;還可能無視參數,直接直接自己的內容,那麼之前的參數就會被忽略。也就是說無所謂順序,404可能是管道的第一環,也可能是最後一環,也可能是中間環節,還可能壓根就不執行。這個和Item1、Item2等內容具體的寫法有關係。(雖然也是鏈式結構是不是和鏈表感覺不一樣

  是不是感覺太零活了,源碼還對ApplicationBuilder做了倆個擴充方法,代碼整理如下:

   public static class RunExtensions    {        public static ApplicationBuilder Use(this ApplicationBuilder app, Func<MyHttpContext, Func<Task>, Task> middleware)        {            return app.Use(next =>            {                return context =>                {                    Func<Task> simpleNext = () => next(context);                    return middleware(context, simpleNext);                };            });        }        public static void Run(this ApplicationBuilder app, RequestDelegate handler)        {            if (app == null)            {                throw new ArgumentNullException("why?");            }            if (handler == null)            {                throw new ArgumentNullException("How?");            }            app.Use(_ => handler);        }    }
View Code

   首先說Use方法,改方法是對之前Use方法的一個更改。將傳入的參數更改為 Func<MyHttpContext, Func<Task>, Task>。這樣做有什麼好處?之前的Func<RequestDelegate, RequestDelegate>對象並不能給人清楚的明了的感覺,而Func<MyHttpContext, Func<Task>, Task>就非常明確了。傳入的參數:MyHttpContext就是Context對象,Func<Task>就是next的執行體。傳回值是一個Task(類似於void)。一目瞭然。

  再說Run方法,顯而易見,Run方法只執行自己的內容,並沒有執行參數體。所以鏈式結構的在其前的都會被捨棄,不會被執行。

 

  最後把自己的測試例子貼出來,供大家參考

樣本1:

static void Main(string[] args)        {            MyHttpContext context = new MyHttpContext() { StatusCode = "A" };            Func<MyHttpContext, Func<Task>, Task> middleware =                (x, y) => { context.StatusCode += "C"; System.Console.WriteLine(context.StatusCode); return y(); };            Func<MyHttpContext, Func<Task>, Task> middleware2 =              (x, y) => { context.StatusCode += "End1"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); };            ApplicationBuilder builder = new ApplicationBuilder();            builder.Use(                next =>                {                    return (MyHttpContext o) =>                    {                        o.StatusCode += "B";                        System.Console.WriteLine(context.StatusCode);                        next(o);                        return Task.FromResult(0);                    };                }            );            builder.Use(middleware);            //builder.Use(middleware2);            //builder.Use(middleware);            //builder.Run(o => { o.StatusCode += "End2"; return Task.FromResult(0); });            builder.Build().Invoke(context);            System.Console.ReadLine();        }
View Code

執行結果:

樣本2:

static void Main(string[] args)        {            MyHttpContext context = new MyHttpContext() { StatusCode = "A" };            Func<MyHttpContext, Func<Task>, Task> middleware =                (x, y) => { context.StatusCode += "C"; System.Console.WriteLine(context.StatusCode); return y(); };            Func<MyHttpContext, Func<Task>, Task> middleware2 =              (x, y) => { context.StatusCode += "End1"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); };            ApplicationBuilder builder = new ApplicationBuilder();            builder.Use(                next =>                {                    return (MyHttpContext o) =>                    {                        o.StatusCode += "B";                        System.Console.WriteLine(context.StatusCode);                        next(o);                        return Task.FromResult(0);                    };                }            );            builder.Use(middleware);            builder.Use(middleware2);            //builder.Use(middleware);            //builder.Run(o => { o.StatusCode += "End2"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); });            builder.Build().Invoke(context);            System.Console.ReadLine();        }
View Code

執行結果:

樣本3:

static void Main(string[] args)        {            MyHttpContext context = new MyHttpContext() { StatusCode = "A" };            Func<MyHttpContext, Func<Task>, Task> middleware =                (x, y) => { context.StatusCode += "C"; System.Console.WriteLine(context.StatusCode); return y(); };            Func<MyHttpContext, Func<Task>, Task> middleware2 =              (x, y) => { context.StatusCode += "End1"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); };            ApplicationBuilder builder = new ApplicationBuilder();            builder.Use(                next =>                {                    return (MyHttpContext o) =>                    {                        o.StatusCode += "B";                        System.Console.WriteLine(context.StatusCode);                        next(o);                        return Task.FromResult(0);                    };                }            );            builder.Use(middleware);            //builder.Use(middleware2);            //builder.Use(middleware);            builder.Run(o => { o.StatusCode += "End2"; System.Console.WriteLine(context.StatusCode); return Task.FromResult(0); });            builder.Build().Invoke(context);            System.Console.ReadLine();        }
View Code

執行結果:

 

[Asp.net 5] ApplicationBuilder詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.