本系列将分析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方法。
以上是经常使用的注册中间件的方式。