HttpContext探究之RequestServices

果小天發表於2024-06-05

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

相關文章