標籤:ext figure var mvc 那是 completed turn 構造 傳回值
在啟動流程文章中提到,在WebHost類中,通過BuildApplication完成http請求處理管道的構建。在來看一下代碼:
。。。。。。 //這個調用的就是Startup.cs類中的Configure方法 configure(builder); //產生中介軟體鏈式結構 return builder.Build();
在架構中,一個中介軟體處理邏輯是使用一個RequestDelegate委託類型來表示的,定義:delegate Task RequestDelegate(HttpContext context)
那是不是我們直接建立委託方法就可以了?答案是否定的,為了形成一個鏈式結構,中間定義跟註冊都有一定的要求。
首先先介紹下如何定義一個中介軟體。定義中介軟體只需要定義一個類即可,但是這個類並不是隨意寫,裡面的結構由一定的要求:
1,類必須包含一個構造方法,這個構造方法的第一個參數必須是一個RequestDelegate類型,這個參數表達的就是當前定義中介軟體的下一個中介軟體。
2,必須包含一個Invoke方法,方法的第一個參數必須是HttpContext類型,傳回值類型必須是Task,Invoke方法中實現中介軟體邏輯
下面是一個中介軟體定義執行個體代碼:
class MiddlewareSample{ private RequestDelegate _next; public MiddlewareSample(RequesetDelegate next) { _next=next; } public Task Invoke(HttpContext context) { //中介軟體邏輯代碼 ...... //調用下一個中介軟體,實現鏈式調用,當然可以根據業務情境去確定是否繼續往下調用 _next(context); } }
再來看下,如何把定義好的中介軟體註冊到管道中。IApplicationBuilder提供了UseMiddleware的擴充方法,通過這個方法就可以把一個中介軟體類型註冊到管道中。那我們來看一下,它裡面到底做了什嗎?
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args) { var applicationServices = app.ApplicationServices; return app.Use(next => { var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); //通過反射擷取Invoke方法資訊 var invokeMethods = methods.Where(m => string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)).ToArray(); if (invokeMethods.Length > 1) { throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName)); } if (invokeMethods.Length == 0) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName)); } var methodinfo = invokeMethods[0]; if (!typeof(Task).IsAssignableFrom(methodinfo.ReturnType)) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, nameof(Task))); } var parameters = methodinfo.GetParameters(); //判斷Invoke方法是否包含HttpContext參數,並且要求是第一個參數 if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext)) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, nameof(HttpContext))); } var ctorArgs = new object[args.Length + 1]; ctorArgs[0] = next; Array.Copy(args, 0, ctorArgs, 1, args.Length); //執行個體化中介軟體類型,並把next最為構造方法的第一個參數傳遞進去 var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs); if (parameters.Length == 1) { //如果invoke方法只包含一個httpcontext參數,直接建立RequestDelegate return (RequestDelegate)methodinfo.CreateDelegate(typeof(RequestDelegate), instance); } //這裡我把它理解成一個RequestDelegate代理委託 var factory = Compile<object>(methodinfo, parameters); return context => { var serviceProvider = context.RequestServices ?? applicationServices; if (serviceProvider == null) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider))); } return factory(instance, context, serviceProvider); }; }); }
在UseMiddleware方法中,其實是通過反射的方式,解析中介軟體定義的類型,最後建立了一個中介軟體工廠委派物件,工廠就是一個Func<RequestDelegate, RequestDelegate>委託類型,它接收一個RequestDelegate中介軟體,然後返回一個新的中介軟體,接收的這個中介軟體參數是新產生的這個中介軟體的下一個中介軟體。建立好工廠後,調用IApplicationBuilder.Use方法,把這個工廠加入到中介軟體工廠列表中,Use方法實現如下:
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { _components.Add(middleware); return this; }
到此只是有了一個中介軟體工廠集合,最後通過調用builder.Build()方法中完成的中介軟體鏈式結構的產生。
public RequestDelegate Build() { RequestDelegate app = context => { context.Response.StatusCode = 404; return TaskCache.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app; }
首先定義了一個返回404錯誤異常的一個RequestDelegate,這個是作為中介軟體的然後把中介軟體集合反轉,依次調用中介軟體工廠來完成中介軟體的初始化。這裡為什麼要反轉,我們來分析下。
假如我們註冊中介軟體順序為1->2->3->4,那_components中工廠的順序就是工廠1->工廠2->工廠3->工廠4,反轉後就成了工廠4->工廠3->工廠2->工廠1,然後進入for迴圈,首先調用工廠4,用返回404錯誤中介軟體作為參數,最後返回一個中介軟體4,然後調用工廠3,把中介軟體4傳遞進去,再產生一個中介軟體,最後調用工廠1,這樣就形成了 中介軟體1->中介軟體2->中介軟體3->中介軟體4->404錯誤中介軟體 這樣的鏈式結構。當一個http請求過來後,就會按照這個順序依次執行對應的中介軟體邏輯。
asp.net core mvc剖析:處理管道構建