無業可守 創新圖強
living innovation

vinbet com浩博手機

當前位置:首頁 > vinbet com浩博手機 >

.NET Core開發日志——RequestDelegate

日期:2019-08-12

本文主要是對.NET Core開發日志——Middleware的補遺,但是會從看起來平平無奇的RequestDelegate開始敘述,所以以其作為標題,也是合情合理。

RequestDelegate是一種委托類型,其全貌為public delegate Task RequestDelegate(HttpContext context),MSDN上對它的解釋,"A function that can process an HTTP request."——處理HTTP請求的函數。唯一參數,是最熟悉不過的HttpContext,返回值則是表示請求處理完成的異步操作類型。

可以將其理解為ASP.NET Core中對一切HTTP請求處理的抽象(委托類型本身可視為函數模板,其實現具有統一的參數列表及返回值類型),沒有它整個框架就失去了對HTTP請求的處理能力。

并且它也是構成Middleware的基石。或者更準確地說參數與返回值都是其的Func<RequestDelegate, RequestDelegate>委托類型正是維持Middleware運轉的核心齒輪。

組裝齒輪的地方位于ApplicationBuilder類之內,其中包含著所有齒輪的集合。

private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();

以及添加齒輪的方法:

public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware){ _components.Add(middleware); return this;}

在Startup類的Configure方法里調用以上ApplicationBuilder的Use方法,就可以完成一個最簡單的Middleware。

public void Configure(IApplicationBuilder app){ app.Use(_ => { return context => { return context.Response.WriteAsync("Hello, World!"); }; });}

齒輪要想變成Middleware,在完成添加后,還需要經過組裝。

public RequestDelegate Build(){ RequestDelegate app = context => { context.Response.StatusCode = 404; return Task.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app;}

Build方法里先定義了最底層的零件——app,context => { context.Response.StatusCode = 404; return Task.CompletedTask; },這段代碼意味著,如果沒有添加任何Middleware的話,ASP.NET Core站點啟動后,會直接出現404的錯誤。

接下的一段,遍歷倒序排列的齒輪,開始正式組裝。

在上述例子里,只使用了一個齒輪:

_ =>{ return context => { return context.Response.WriteAsync("Hello, World!"); }; }

那么第一次也是最后一次循環后,執行component(app)操作,app被重新賦值為:

context => context.Response.WriteAsync("Hello, World!");

組裝的結果便是app的值。

這個組裝過程在WebHost進行BuildApplication時開始操作。從此方法的返回值類型可以看出,雖然明義上是創建Application,其實生成的是RequestDelegate。

private RequestDelegate BuildApplication(){ try { ... var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>(); var builder = builderFactory.CreateBuilder(Server.Features); ... Action<IApplicationBuilder> configure = _startup.Configure; ... configure(builder); return builder.Build(); } ...}

而這個RequestDelegate最終會在HostingApplication類的ProcessRequestAsync方法里被調用。

public virtual async Task StartAsync(CancellationToken cancellationToken = default){ ... var application = BuildApplication(); ... var hostingApp = new HostingApplication(application, _logger, diagnosticSource, httpContextFactory); ...} public HostingApplication( RequestDelegate application, ILogger logger, DiagnosticListener diagnosticSource, IHttpContextFactory httpContextFactory){ _application = application; _diagnostics = new HostingApplicationDiagnostics(logger, diagnosticSource); _httpContextFactory = httpContextFactory;}public Task ProcessRequestAsync(Context context){ return _application(context.HttpContext);}

上例中的執行結果即是顯示Hello, World!字符。

404的錯誤不再出現,意味著這種Middleware只會完成自己對HTTP請求的處理,并不會將請求傳至下一層的Middleware。

要想達成不斷傳遞請求的目的,需要使用另一種Use擴展方法。

public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware){ return app.Use(next => { return context => { Func<Task> simpleNext = () => next(context); return middleware(context, simpleNext); }; });}

在實際代碼中可以這么寫:

public void Configure(IApplicationBuilder app){ app.Use(async (context, next) => { await context.Response.WriteAsync("I am a Middleware!"); await next.Invoke(); }); app.Use(_ => { return context => { return context.Response.WriteAsync("Hello, World!"); }; });}

現在多了個Middleware,繼續上面的組裝過程。app的值最終被賦值為:

async context =>{ Func<Task> simpleNext = () => context.Response.WriteAsync("Hello, World!"); await context.Response.WriteAsync("I am a Middleware!"); await simpleNext.Invoke();};

顯示結果為:

I am a Middleware!Hello, World!

下面的流程圖中可以清楚地說明這個過程。

如果把await next.Invoke()注釋掉的話,

public void Configure(IApplicationBuilder app){ app.Use(async (context, next) => { await context.Response.WriteAsync("I am a Middleware!"); //await next.Invoke(); }); app.Use(_ => { return context => { return context.Response.WriteAsync("Hello, World!"); }; });}

上例中第一個Middleware處理完后,不會繼續交給第二個Middleware處理。注意以下simpleNext的方法只被定義而沒有被調用。

async context =>{ Func<Task> simpleNext = () => context.Response.WriteAsync("Hello, World!"); await context.Response.WriteAsync("I am a Middleware!");};

這種情況被稱為短路(short-circuiting)。

做短路處理的Middleware一般會放在所有Middleware的最后,以作為整個pipeline的終點。

并且更常見的方式是用Run擴展方法。

public static void Run(this IApplicationBuilder app, RequestDelegate handler){ ... app.Use(_ => handler);}

所以可以把上面例子的代碼改成下面的形式:

public void Configure(IApplicationBuilder app){ app.Use(async (context, next) => { await context.Response.WriteAsync("I am a Middleware!"); await next.Invoke(); }); app.Run(async context => { await context.Response.WriteAsync("Hello, World!"); });}

除了短路之外,Middleware處理時還可以有分支的情況。

public void Configure(IApplicationBuilder app){ app.Map("/branch1", ab => { ab.Run(async context => { await context.Response.WriteAsync("Map branch 1"); }); }); app.Map("/branch2", ab => { ab.Run(async context => { await context.Response.WriteAsync("Map branch 2"); }); }); app.Use(async (context, next) => { await context.Response.WriteAsync("I am a Middleware!"); await next.Invoke(); }); app.Run(async context => { await context.Response.WriteAsync("Hello, World!"); });}

URL地址后面跟著branch1時:

URL地址后面跟著branch2時:

其它情況下:

Map擴展方法的代碼實現:

public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration){ ... // create branch var branchBuilder = app.New(); configuration(branchBuilder); var branch = branchBuilder.Build(); var options = new MapOptions { Branch = branch, PathMatch = pathMatch, }; return app.Use(next => new MapMiddleware(next, options).Invoke);}

創建分支的辦法就是重新實例化一個ApplicationBuilder。

public IApplicationBuilder New(){ return new ApplicationBuilder(this);}

對分支的處理則是封裝在MapMiddleware類之中。

public async Task Invoke(HttpContext context){ ... PathString matchedPath; PathString remainingPath; if (context.Request.Path.StartsWithSegments(_options.PathMatch, out matchedPath, out remainingPath)) { // Update the path var path = context.Request.Path; var pathBase = context.Request.PathBase; context.Request.PathBase = pathBase.Add(matchedPath); context.Request.Path = remainingPath; try { await _options.Branch(context); } finally { context.Request.PathBase = pathBase; context.Request.Path = path; } } else { await _next(context); }}

說到MapMiddleware,不得不提及各種以Use開頭的擴展方法,比如UseStaticFiles,UseMvc,UsePathBase等等。

這些方法內部都會調用UseMiddleware方法以使用各類定制的Middleware類。如下面UsePathBase的代碼:

public static IApplicationBuilder UsePathBase(this IApplicationBuilder app, PathString pathBase){ ... // Strip trailing slashes pathBase = pathBase.Value?.TrimEnd("/"); if (!pathBase.HasValue) { return app; } return app.UseMiddleware<UsePathBaseMiddleware>(pathBase);}

而從UseMiddleware方法中可以獲知,Middleware類需滿足兩者條件之一才能被有效使用。其一是實現IMiddleware,其二,必須有Invoke或者InvokeAsync方法,且方法至少要有一個HttpContext類型參數(它還只能是放第一個),同時返回值需要是Task類型。

internal const string InvokeMethodName = "Invoke";internal const string InvokeAsyncMethodName = "InvokeAsync";public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args){ if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo())) { ... return UseMiddlewareInterface(app, middleware); } var applicationServices = app.ApplicationServices; return app.Use(next => { var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); var invokeMethods = methods.Where(m => string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal) || string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal) ).ToArray(); ... var ctorArgs = new object[args.Length + 1]; ctorArgs[0] = next; Array.Copy(args, 0, ctorArgs, 1, args.Length); var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs); if (parameters.Length == 1) { return (RequestDelegate)methodinfo.CreateDelegate(typeof(RequestDelegate), instance); } var factory = Compile<object>(methodinfo, parameters); return context => { var serviceProvider = context.RequestServices ?? applicationServices; ... return factory(instance, context, serviceProvider); }; });}

對ASP.NET Core中Middleware的介紹到此終于可以告一段落,希望這兩篇文章能夠為讀者提供些許助力。

天天看片