本系列將分析ASP.NET Core運行原理html
本節將分析WebHost.StartAsync();
代碼,肯定是如何一步一步到咱們註冊的中間件,並介紹幾種Configure的方式。git
源代碼參考.NET Core 2.0.0github
在上節咱們知道WebHost.StartAsync
內部是調用Server.StartAsync
的。c#
public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) { async Task OnBind(ListenOptions endpoint) { var connectionHandler = new ConnectionHandler<TContext>(endpoint, ServiceContext, application); var transport = _transportFactory.Create(endpoint, connectionHandler); _transports.Add(transport); await transport.BindAsync().ConfigureAwait(false); } await AddressBinder.BindAsync(_serverAddresses, Options.ListenOptions, Trace, OnBind).ConfigureAwait(false); }
參數application即爲以前的new HostingApplication
。在這裏說下大概的流程:app
KestrelServer.StartAsync -> new ConnectionHandler<TContext>().OnConnection -> new FrameConnection().StartRequestProcessing() -> new Frame<TContext>().ProcessRequestsAsync() -> _application.CreateContext(this) && _application.ProcessRequestAsync(context)
若是你須要更細節的流程,可參考以下:socket
LibuvTransportFactory -> LibuvTransport.BindAsync() -> ListenerPrimary.StartAsync() -> listener.ListenSocket.Listen(LibuvConstants.ListenBacklog, ConnectionCallback, listener) -> listener.OnConnection(stream, status) -> ConnectionCallback() -> new LibuvConnection(this, socket).Start() -> ConnectionHandler.OnConnection() -> connection.StartRequestProcessing() -> ProcessRequestsAsync -> CreateFrame -> await _frame.ProcessRequestsAsync()
下面展現Frame以及HostingApplication:async
public class Frame<TContext> : Frame { public override async Task ProcessRequestsAsync() { while (!_requestProcessingStopping) { Reset(); EnsureHostHeaderExists(); var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this); InitializeStreams(messageBody); var context = _application.CreateContext(this); try { await _application.ProcessRequestAsync(context); } finally { _application.DisposeContext(context, _applicationException); } } } }
public class HostingApplication : IHttpApplication<HostingApplication.Context> { private readonly RequestDelegate _application; private readonly IHttpContextFactory _httpContextFactory; public HostingApplication( RequestDelegate application, IHttpContextFactory httpContextFactory) { _application = application; _httpContextFactory = httpContextFactory; } // Set up the request public Context CreateContext(IFeatureCollection contextFeatures) { var context = new Context(); var httpContext = _httpContextFactory.Create(contextFeatures); context.HttpContext = httpContext; return context; } // Execute the request public Task ProcessRequestAsync(Context context) { return _application(context.HttpContext); } // Clean up the request public void DisposeContext(Context context, Exception exception) { var httpContext = context.HttpContext; _httpContextFactory.Dispose(httpContext); } public struct Context { public HttpContext HttpContext { get; set; } } }
由此咱們發現HttpContext是由HttpContextFactory建立的,其中_httpContextFactory
則是上節在WebHostBuilder的BuildCommon注入的
同時在HostingApplication的ProcessRequestAsync方法中,咱們看到咱們的_application(Startup註冊的中間件)被調用了。
IHttpContextFactory。ide
public HttpContext Create(IFeatureCollection featureCollection) { var httpContext = new DefaultHttpContext(featureCollection); if (_httpContextAccessor != null) _httpContextAccessor.HttpContext = httpContext; return httpContext; }
而建立的HttpContext則是DefaultHttpContext類型:函數
public class DefaultHttpContext : HttpContext { public virtual void Initialize(IFeatureCollection features) { _features = new FeatureReferences<FeatureInterfaces>(features); _request = InitializeHttpRequest(); _response = InitializeHttpResponse(); } public override HttpRequest Request => _request; public override HttpResponse Response => _response; }
咱們知道在Startup的Configure方法中,經過IApplicationBuilder
能夠註冊中間件。ui
public interface IApplicationBuilder { IServiceProvider ApplicationServices { get; set; } RequestDelegate Build(); IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); }
默認實現類爲:
public class ApplicationBuilder : IApplicationBuilder { 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 app = context => { context.Response.StatusCode = 404; return Task.CompletedTask; }; foreach (var component in _components.Reverse()) app = component(app); return app; } }
其中Use方法爲註冊中間件。中間件的本質就是一個Func<RequestDelegate, RequestDelegate>
對象。
該對象的傳入參數爲下一個中間件,返回對象爲本中間件。
而Build方法爲生成一個RequestDelegate
,在HostingApplication構造函數中的參數即爲該對象。
在Build方法中,咱們看到最後一箇中間件爲404中間件。其餘的中間件都是經過Use方法註冊到內部維護的_components對象上。
咱們經過一個Use示例,來看下中間件的流程:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Use(next => async context => { Console.WriteLine("A begin"); await next(context); Console.WriteLine("A end"); }); app.Use(next => async context => { Console.WriteLine("B begin"); await next(context); Console.WriteLine("B end"); }); }
訪問結果:
A begin
B begin
B end
A end
流程圖:
當咱們不使用next 下一個中間件的時候,咱們可使用Run方法來實現
Run方法接受一個RequestDelegate對象,自己是IApplicationBuilder的擴展方法。
public static void Run(this IApplicationBuilder app, RequestDelegate handler); { app.Use(_ => handler); }
Run示例
app.Run(context=>context.Response.WriteAsync("Run Core"));
該示例至關於:
app.Use(next => context => context.Response.WriteAsync("Run Core"));
而一般咱們添加中間件的方式是經過UseMiddleware來更加方便的操做。
先看下IMiddleware:
public interface IMiddleware { Task InvokeAsync(HttpContext context, RequestDelegate next); }
參數next即爲下一個中間件。
有2種實現UseMiddleware的方式:
public class DemoMiddle : IMiddleware { public Task InvokeAsync(HttpContext context, RequestDelegate next) { return context.Response.WriteAsync("hello middleware"); } }
在使用IMiddleware接口的時候,還須要註冊該類到DI系統中。
public class DemoMiddle { private RequestDelegate _next; public DemoMiddle(RequestDelegate next) { _next = next; } public Task InvokeAsync(HttpContext context) { return context.Response.WriteAsync("hello middleware"); } }
這種方式,不用再註冊到DI中,若是須要對該類構造函數傳入參數,直接在app.UseMiddleware<DemoMiddle>("hi1");
傳入參數便可。
app.Use(next => async context => { await context.Response.WriteAsync("Begin"); await next(context); }); app.UseWhen(context => context.Request.Path.Value == "/hello", branch => branch.Use( next => async context => { await context.Response.WriteAsync("hello"); await next(context); })); app.Run(context => context.Response.WriteAsync("End"));
當咱們訪問/hello時,結果爲:BeginhelloEnd
分析源碼得知在構建管道的時候,克隆一個另外的IApplicationBuilder。
public static IApplicationBuilder UseWhen(this IApplicationBuilder app, Predicate predicate, Action<IApplicationBuilder> configuration) { var branchBuilder = app.New(); configuration(branchBuilder); return app.Use(main => { // This is called only when the main application builder // is built, not per request. branchBuilder.Run(main);// 添加(調用)原來的中間件 var branch = branchBuilder.Build(); return context => predicate(context) ? branch(context): main(context); }); }
app.Use(next => async context => { await context.Response.WriteAsync("Begin"); await next(context); }); app.MapWhen(context => context.Request.Path.Value == "/hello", app2 => app2.Run(context => context.Response.WriteAsync("hello"))); app.Run(context => context.Response.WriteAsync("End"));
當咱們訪問/hello時,結果爲:Beginhello
。
分析源碼得知在構建管道的時候,新分支並無再調用原來的中間件。
public static IApplicationBuilder MapWhen(this IApplicationBuilder app, Predicate predicate, Action<IApplicationBuilder> configuration) { var branchBuilder = app.New(); configuration(branchBuilder); var branch = branchBuilder.Build(); return app.Use(next => context => predicate(context) ? branch(context): next(context)); }
app.Map("/hello", app2 => app2.Run(context => context.Response.WriteAsync("hello")));
當咱們訪問/hello時,結果爲:Beginhello
。與MapWhen效果同樣。
若是咱們只是判斷URLPath的話,一般咱們會使用Map方法。
以上是經常使用的註冊中間件的方式。