HttpContext探究之RequestServices

HttpContext探究之RequestServices

在一篇随笔中提到了中间件的构造方式,主要有两种,第一种是直接从容器里面获取,第二种是构造函数的参数从容器里面获取,这两者都离不开容器,也就是serviceprovider,而RequestServices则是里面重要的内容

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类的构建,此篇文章 Asp.net core mapcontrollers 背后干了些啥有提到,

相关创建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

posted @ 2024-06-05 23:46  果小天  阅读(78)  评论(0编辑  收藏  举报