首页 > 其他分享 >HttpContext探究之RequestServices

HttpContext探究之RequestServices

时间:2024-06-05 23:55:14浏览次数:20  
标签:RequestServices public 探究 context ServiceProvidersFeature HttpContext httpContex

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

标签:RequestServices,public,探究,context,ServiceProvidersFeature,HttpContext,httpContex
From: https://www.cnblogs.com/guoxiaotian/p/18234191

相关文章

  • 两颗i9-14900KS深度评测:探究不稳定的原因及解决办法
    一、前言:生来坎坷的i9-14900KS这是Intel最后一颗使用Intel7制程工艺的桌面处理器,可能也是多年来命途最为坎坷的顶级游戏处理器!我们早在3月份就收到了i9-14900KS,但因各种原因,直到今天才让大家见到这篇评测文章。i9-14900KS在规格上与i9-14900K相似,核心代号RaptorLake-SRefre......
  • 【科普向】【文末附gpt升级秘笈】《庆余年》凤冠之工艺探究——Blender建模与3D打印之
    《庆余年》凤冠之工艺探究——Blender建模与3D打印之奥秘一、引言昔者,《庆余年》之热播,引发天下观众之热议。今者,其续作《庆余年2》之中,一场盛大的婚礼更是瞩目。而此婚礼之上,唯一之凤冠,竟出自一款名为Blender之软件之手,辅以3D打印之技术,成就其非凡之美。夫此软件,诞生于三十......
  • 【机器学习】Chameleon多模态模型探究
    Chameleon:引领多模态模型的新时代一、多模态模型的时代背景二、Chameleon模型的介绍三、Chameleon模型的技术特点四、Chameleon模型的性能评估五、Chameleon模型的代码实例随着人工智能技术的深入发展,我们逐渐认识到单一模态的模型在处理复杂问题时存在一定的局限......
  • I. NeRF 及其衍生算法的初步探究
    I.NeRF及其衍生算法的初步探究视频链接:【AI講壇】NeRF與它的快樂夥伴們[Neuralradiancefields]NeRF的主要优势:能够正确处理反光、估算的深度较准、等等。一、nerfinthewildGoogleResearch、未开源NeRFintheWild:NeuralRadianceFieldsforUnconstrainedPhot......
  • I. NeRF及其衍生算法的初步探究
    视频链接:【AI講壇】NeRF與它的快樂夥伴們[Neuralradiancefields]NeRF的主要优势:能够正确处理反光、估算的深度较准、等等。一、nerfinthewildGoogleResearch、未开源NeRFintheWild:NeuralRadianceFieldsforUnconstrainedPhotoCollections.CVPR2021(Oral)......
  • 农村高中生源转型期提升学生二次函数建模能力的课堂探究
      在新课程下,培养学生的数学核心素养是高中数学课堂教学的根本任务。其中的建模思想是数学核心素养培养的一个基本指标,是学生正确认识数学知识内在本质与原理的重要思维工具。通过在数学课堂教学中有效地应用建模思想,主要的应用意义体现在如下几个方面:其一,通过在数学课堂中融入......
  • 数据结构第一篇【探究List和ArrayList之间的奥秘 】
    数据结构第一篇【探究List和ArrayList之间的奥秘】前言List什么是List?ListArrayListArrayList使用ArrayList常见操作ArrayList的遍历ArrayList的扩容机制ArrayList的具体使用前言......
  • 探究-百万级别excel数据的导入导出
    思路:1.从数据库中读,分批次读取2.多线程去写到一个excel中,3.网页如何不卡界面的方式,下载文件界面显示进度条,不影响主界面,下载完成,网站弹出小框口提示百万级别数据导入导出如何优化?excel导入导出;痛点:1.导入百万级别数据(excel)到DB,有哪些痛点?1)一次加载百万级别数据到内存,发生OOM!2)导......
  • 农村高中生源转型期提升学生二次函数建模能力的课堂探究
       通过结合具体的数学问题,引导高中生深入分析问题,有效地构建求解问题的数学模型,可以使学生逐步掌握数学问题求解的基本思路以及模型建构的方法与注意事项。但是离开了反复训练,无法从根本上提升高中生的数学建模能力。因此,在平时的高中数学教学中,教师要注意结合数学教学的内......
  • 探究——C# .net 代码混淆/加壳
    背景:保密。过程:先查询一下常见的加壳工具:DotFuscator,官方自带,据说免费版混淆程度不高ConfuserEx,只支持.NETFramework2.0/3.0/3.5/4.0/4.5/4.6/4.7/4.8,不支持.NETCoreVirboxProtector,很好很优秀,但是收费NETReactor,最新6.9版收费,PJ版到4.9不支持.NETCoreObfu......