HttpContext探究之RequestServices
在一篇隨筆中提到了中介軟體的構造方式,主要有兩種,第一種是直接從容器裡面獲取,第二種是建構函式的引數從容器裡面獲取,這兩者都離不開容器,也就是serviceprovide,而RequestService則是裡面重要的內容RequestServices是什麼
HttpContext.RequestServices
是一個IServiceProvider
例項,它代表了當前 HTTP 請求的作用域服務容器。這個服務容器是在請求處理管道的早期階段建立的,並附加到 HttpContext 物件上。說人話就是隨著httpcontext一起建立的。如何建立
首先看看
HttpContext
結構 :httpcontext
預設實現是defaulthttpcontext
,程式碼中其他的都刪除了,只保留了requestservice相關以及建構函式。
defaulthttpcontext
由IHttpContextFactory
的預設實現類DefaultHttpContextFactory
建立DefaultHttpContext
public sealed class DefaultHttpContext : HttpContext { //省略一堆程式碼 private static readonly Func<DefaultHttpContext, IServiceProvidersFeature> _newServiceProvidersFeature = context => new RequestServicesFeature(context, context.ServiceScopeFactory); private readonly DefaultHttpRequest _request; private readonly DefaultHttpResponse _response; public DefaultHttpContext() : this(new FeatureCollection(DefaultFeatureCollectionSize)) { Features.Set<IHttpRequestFeature>(new HttpRequestFeature()); Features.Set<IHttpResponseFeature>(new HttpResponseFeature()); Features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null)); } public IServiceScopeFactory ServiceScopeFactory { get; set; } = default!; private IServiceProvidersFeature ServiceProvidersFeature => _features.Fetch(ref _features.Cache.ServiceProviders, this, _newServiceProvidersFeature)!; public override IServiceProvider RequestServices { get { return ServiceProvidersFeature.RequestServices; } set { ServiceProvidersFeature.RequestServices = value; } } }
DefaultHttpContextFactory
public class DefaultHttpContextFactory : IHttpContextFactory { private readonly IHttpContextAccessor? _httpContextAccessor; private readonly FormOptions _formOptions; private readonly IServiceScopeFactory _serviceScopeFactory; public DefaultHttpContextFactory(IServiceProvider serviceProvider) { // May be null _httpContextAccessor = serviceProvider.GetService<IHttpContextAccessor>(); _formOptions = serviceProvider.GetRequiredService<IOptions<FormOptions>>().Value; _serviceScopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>(); } internal IHttpContextAccessor? HttpContextAccessor => _httpContextAccessor; public HttpContext Create(IFeatureCollection featureCollection) { ArgumentNullException.ThrowIfNull(featureCollection); var httpContext = new DefaultHttpContext(featureCollection); Initialize(httpContext, featureCollection); return httpContext; } internal void Initialize(DefaultHttpContext httpContext, IFeatureCollection featureCollection) { httpContext.Initialize(featureCollection); if (_httpContextAccessor != null) { _httpContextAccessor.HttpContext = httpContext; } httpContext.FormOptions = _formOptions; httpContext.ServiceScopeFactory = _serviceScopeFactory; } }
DefaultHttpContextFactory.Create
方法裡面建立DefaultHttpContext, 並進行初始化賦值:httpContext.ServiceScopeFactory = _serviceScopeFactory;
而_serviceScopeFactory
是從服務中獲取的來的,同時也是註冊為單例生命週期。接著在
DefaultHttpContext
中 利用serviceScopeFactory 去構建一個叫做Func<DefaultHttpContext, IServiceProvidersFeature> _newServiceProvidersFeature = context => new RequestServicesFeature(context, context.ServiceScopeFactory)
;一個這樣的委託,同時使用一個叫做
ServiceProvidersFeature
的屬性獲取去RequestServicesFeature
private IServiceProvidersFeature ServiceProvidersFeature => _features.Fetch(ref _features.Cache.ServiceProviders, this, _newServiceProvidersFeature)!;
看起來很繞,實際上確實有點繞,:)
_features.Fetch(ref _features.Cache.ServiceProviders, this, _newServiceProvidersFeature)!;
說人話就是先從快取的ref _features.Cache.ServiceProviders獲取,如果沒有的話,就呼叫_newServiceProvidersFeature
這個委託獲取,引數就是this,即defaulthttpcontext。然後賦值給_features.Cache.ServiceProviders**核心要點保證獲取的是同一個 **
ServiceProvidersFeature `,
不會每次都建立一個新的 IServiceProvidersFeature,這一點很重要。最後就是
RequestServices
,直接從ServiceProvidersFeature.RequestServices
中獲取public override IServiceProvider RequestServices { get { return ServiceProvidersFeature.RequestServices; } set { ServiceProvidersFeature.RequestServices = value; } } public IServiceProvider RequestServices { get { if (!_requestServicesSet && _scopeFactory != null) { _context.Response.RegisterForDisposeAsync(this); _scope = _scopeFactory.CreateScope(); _requestServices = _scope.ServiceProvider; _requestServicesSet = true; } return _requestServices!; } set { _requestServices = value; _requestServicesSet = true; } }
同樣也是為了保證每次獲得的
_requestServices
都是同一個,初始化的時候是使用_scopeFactory.CreateScope(); 即建立了一個服務域,然後本次請求的所有服務都從此容器中獲取,所以範圍單例的服務生命週期預設為一次請求,就可以理解了。有什麼用
上面說到一次請求中的所有服務都從requestServices 中獲取,這就是他最大的作用,作為一個依賴注入容器,我們看看它是如何被使用的。
比如上篇文章裡面涉及到了如何構建基於介面實現的中介軟體
public RequestDelegate CreateMiddleware(RequestDelegate next) { return async context => { //di中獲取middlewareFactory var middlewareFactory = (IMiddlewareFactory?)context.RequestServices.GetService(typeof(IMiddlewareFactory)); //省略一些判斷,利用middlewareFactory構造我們的中介軟體,實際上就是從di中獲取, //只不過包裹了一層 var middleware = middlewareFactory.Create(_middlewareType); //省略一些判斷 try { await middleware.InvokeAsync(context, next); } finally { middlewareFactory.Release(middleware); } }; }
這裡的
IMiddlewareFactory
就是從context.RequestServices
中獲取的,也說明了此種型別中介軟體是每次請求都會被建立一次,而不是整個應用生命週期只建立一次。同時還有我們的controller類的構建,此篇
有提到,相關建立controller程式碼:
public object CreateController(ControllerContext context) { //重點程式碼 var controller = _controllerActivator.Create(context); oreach (var propertyActivator in _propertyActivators) { propertyActivator.Activate(context, controller); } return controller; } public object Create(ControllerContext controllerContext) { //核心程式碼 刪除了一些程式碼 var controllerTypeInfo = controllerContext.ActionDescriptor.ControllerTypeInfo; var serviceProvider = controllerContext.HttpContext.RequestServices; return _typeActivatorCache.CreateInstance<object>(serviceProvider, controllerTypeInfo.AsType()); }
看到程式碼中我們的controller物件也是透過
controllerContext.HttpContext.RequestServices
提供的構造引數去構建。總結
大概介紹了一下httpcotext的RequestServices,是如何根據每次請求被建立的,以及有什麼樣的作用,以後寫bug應該會更加得心應手把 :)
參考文章:
微軟官方文件
以及網站 :https://source.dot.net/#Microsoft.AspNetCore.Hosting/Http/DefaultHttpContextFactory.cs,a66c2cafba21597c