zoukankan      html  css  js  c++  java
  • Asp.net web Api源码分析HttpRequestMessage的创建

    紧接着前文Asp.net web Api源码分析-如何获取IHttpHandler 我们已经得到了HttpControllerHandler实例,它是一个IHttpAsyncHandler类型,我们来看看它的BeginProcessRequest方法是如何实现:

      protected virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContextBase, AsyncCallback callback, object state)
            {
                HttpRequestMessage request = httpContextBase.GetHttpRequestMessage() ?? ConvertRequest(httpContextBase);
                request.Properties[HttpPropertyKeys.HttpRouteDataKey] = _routeData;
                Task responseBodyTask = _server.Value.SendAsync(request, CancellationToken.None)
                    .Then(response => ConvertResponse(httpContextBase, response, request));
    
                TaskWrapperAsyncResult result = new TaskWrapperAsyncResult(responseBodyTask, state);
                if (callback != null)
                {
                    if (result.IsCompleted)
                    {
                        result.CompletedSynchronously = true;
                        callback(result);
                    }
                    else
                    {
                        result.CompletedSynchronously = false;
                        responseBodyTask.ContinueWith(_ =>
                        {
                            callback(result);
                        });
                    }
                }
    
                return result;
            }
    

     我想这个方法的大致逻辑大家一看也就明白了,这里我们只关心  HttpRequestMessage request = httpContextBase.GetHttpRequestMessage() ?? ConvertRequest(httpContextBase);这句,创建HttpRequestMessage实例,有关 HttpRequestMessage的一些介绍大家可以参考MVC4 WebAPI(二)——Web API工作方式
    其中GetHttpRequestMessage方法的实现非常简单:

    internal static readonly string HttpRequestMessageKey = "MS_HttpRequestMessage";
    public static HttpRequestMessage GetHttpRequestMessage(this HttpContextBase context)
    {
         return context.Items[HttpRequestMessageKey] as HttpRequestMessage;
    }

    public static void SetHttpRequestMessage(this HttpContextBase context, HttpRequestMessage request)
    {
        context.Items.Add(HttpRequestMessageKey, request);
    }

    所以这里的GetHttpRequestMessage并没有真正创建HttpRequestMessage,ConvertRequest方法才是真正创建HttpRequestMessage的地方。

      internal static HttpRequestMessage ConvertRequest(HttpContextBase httpContextBase)
            {
                Contract.Assert(httpContextBase != null);
    
                HttpRequestBase requestBase = httpContextBase.Request;
                HttpMethod method = HttpMethodHelper.GetHttpMethod(requestBase.HttpMethod);
                Uri uri = requestBase.Url;
                HttpRequestMessage request = new HttpRequestMessage(method, uri);
    
                IHostBufferPolicySelector policySelector = _bufferPolicySelector.Value;
                bool isInputBuffered = policySelector == null ? true : policySelector.UseBufferedInputStream(httpContextBase);
                Stream inputStream = isInputBuffered
                                        ? requestBase.InputStream
                                        : httpContextBase.ApplicationInstance.Request.GetBufferlessInputStream();
    
                request.Content = new StreamContent(inputStream);
                foreach (string headerName in requestBase.Headers)
                {
                    string[] values = requestBase.Headers.GetValues(headerName);
                    AddHeaderToHttpRequestMessage(request, headerName, values);
                }
                request.Properties.Add(HttpContextBaseKey, httpContextBase);
                request.Properties.Add(HttpPropertyKeys.RetrieveClientCertificateDelegateKey, _retrieveClientCertificate);
                request.Properties.Add(HttpPropertyKeys.IsLocalKey, new Lazy<bool>(() => requestBase.IsLocal));
                request.Properties.Add(HttpPropertyKeys.IncludeErrorDetailKey, new Lazy<bool>(() => !httpContextBase.IsCustomErrorEnabled));
    
                return request;
            }
    

     其中

        HttpRequestBase requestBase = httpContextBase.Request;
                HttpMethod method = HttpMethodHelper.GetHttpMethod(requestBase.HttpMethod);
                Uri uri = requestBase.Url;
                HttpRequestMessage request = new HttpRequestMessage(method, uri);

    这几句代码很简单也很好明白,我想我就不多说了,而下面的几句代码页很好理解

        IHostBufferPolicySelector policySelector = _bufferPolicySelector.Value;
                bool isInputBuffered = policySelector == null ? true : policySelector.UseBufferedInputStream(httpContextBase);
                Stream inputStream = isInputBuffered
                                        ? requestBase.InputStream
                                        : httpContextBase.ApplicationInstance.Request.GetBufferlessInputStream();

     request.Content = new StreamContent(inputStream);

    isInputBuffered是否使用输入缓存,这里默认返回true,这几句主要是 获取请求的输入流isInputBuffered? requestBase.InputStream: httpContextBase.ApplicationInstance.Request.GetBufferlessInputStream();, 然后设置HttpRequestMessage的Content属性

     foreach (string headerName in requestBase.Headers)
                {
                    string[] values = requestBase.Headers.GetValues(headerName);
                    AddHeaderToHttpRequestMessage(request, headerName, values);
                }

    这句也很好明白,就是把Request的header信息按需添加到HttpRequestMessage的Content.Headers里面, 最后在HttpRequestMessage的Properties属性中添加一些暂存信息。这里的Properties属性没得说它是 IDictionary<string, object>类型。
    现在我们好好分析一下这里面的一些细节的东西。
    首先我们来看看    IHostBufferPolicySelector policySelector = _bufferPolicySelector.Value;这一句,这里的_bufferPolicySelector是一个延迟加载对象,其定义如下:

      private static readonly Lazy<IHostBufferPolicySelector> _bufferPolicySelector =
                new Lazy<IHostBufferPolicySelector>(() => GlobalConfiguration.Configuration.Services.GetHostBufferPolicySelector());

    在GlobalConfiguration类中有这么一句

        config.Services.Replace(typeof(IHostBufferPolicySelector), new WebHostBufferPolicySelector());这里我们可以知道_bufferPolicySelector.Value其实就是一个WebHostBufferPolicySelector实例,该实例的UseBufferedInputStream方法返回true,表示使用输入缓存。

    大家应该还记得前面在说HttpConfiguration的构造函数有这么一句吧:

       Services = new DefaultServices(this); 其中Services是ServicesContainer类型

    DefaultServices的代码比较多但是还是比较好理解的,

        private readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim();
        private readonly Dictionary<Type, object[]> _cacheMulti = new Dictionary<Type, object[]>();
        private readonly Dictionary<Type, object> _cacheSingle = new Dictionary<Type, object>();
        private readonly Dictionary<Type, object> _defaultServicesSingle = new Dictionary<Type, object>();
        private readonly Dictionary<Type, List<object>> _defaultServicesMulti = new Dictionary<Type, List<object>>();
    
    
        public DefaultServices(HttpConfiguration configuration)
            {
                if (configuration == null)
                {
                    throw Error.ArgumentNull("configuration");
                }
    
                _configuration = configuration;
    
                // Initialize the dictionary with all known service types, even if the list for that service type is
                // empty, because we will throw if the developer tries to read or write unsupported types.
    
                SetSingle<IActionValueBinder>(new DefaultActionValueBinder());
                SetSingle<IApiExplorer>(new ApiExplorer(configuration));
                SetSingle<IAssembliesResolver>(new DefaultAssembliesResolver());
                SetSingle<IBodyModelValidator>(new DefaultBodyModelValidator());
                SetSingle<IContentNegotiator>(new DefaultContentNegotiator());
                SetSingle<IDocumentationProvider>(null); // Missing
    
                SetMultiple<IFilterProvider>(new ConfigurationFilterProvider(),
                                          new ActionDescriptorFilterProvider());
    
                SetSingle<IHostBufferPolicySelector>(null);
                SetSingle<IHttpActionInvoker>(new ApiControllerActionInvoker());
                SetSingle<IHttpActionSelector>(new ApiControllerActionSelector());
                SetSingle<IHttpControllerActivator>(new DefaultHttpControllerActivator());
                SetSingle<IHttpControllerSelector>(new DefaultHttpControllerSelector(configuration));
                SetSingle<IHttpControllerTypeResolver>(new DefaultHttpControllerTypeResolver());
                SetSingle<ITraceManager>(new TraceManager());
                SetSingle<ITraceWriter>(null);
    
                // This is a priority list. So put the most common binders at the top. 
                SetMultiple<ModelBinderProvider>(new TypeConverterModelBinderProvider(),
                                            new TypeMatchModelBinderProvider(),
                                            new KeyValuePairModelBinderProvider(),
                                            new ComplexModelDtoModelBinderProvider(),
                                            new ArrayModelBinderProvider(),
                                            new DictionaryModelBinderProvider(),
                                            new CollectionModelBinderProvider(),
                                            new MutableObjectModelBinderProvider());
                SetSingle<ModelMetadataProvider>(new DataAnnotationsModelMetadataProvider());
                SetMultiple<ModelValidatorProvider>(new DataAnnotationsModelValidatorProvider(),
                                            new DataMemberModelValidatorProvider(),
                                            new InvalidModelValidatorProvider());
    
                // This is an ordered list,so put the most common providers at the top. 
                SetMultiple<ValueProviderFactory>(new QueryStringValueProviderFactory(),
                                               new RouteDataValueProviderFactory());
    
                ModelValidatorCache validatorCache = new ModelValidatorCache(new Lazy<IEnumerable<ModelValidatorProvider>>(() => this.GetModelValidatorProviders()));
                configuration.RegisterForDispose(validatorCache);
                SetSingle<IModelValidatorCache>(validatorCache);
    
                _serviceTypesSingle = new HashSet<Type>(_defaultServicesSingle.Keys);
                _serviceTypesMulti = new HashSet<Type>(_defaultServicesMulti.Keys);
    
                // Reset the caches and the known dependency scope
                ResetCache();
            }
           private void SetSingle<T>(T instance) where T : class
            {
                _defaultServicesSingle[typeof(T)] = instance;
            }
            private void SetMultiple<T>(params T[] instances) where T : class
            {
                var x = (IEnumerable<object>)instances;
                _defaultServicesMulti[typeof(T)] = new List<object>(x);
            }
           private void ResetCache()
            {
                _cacheLock.EnterWriteLock();
                try
                {
                    _cacheSingle.Clear();
                    _cacheMulti.Clear();
                    _lastKnownDependencyResolver = _configuration.DependencyResolver;
                }
                finally
                {
                    _cacheLock.ExitWriteLock();
                }
            }
          public override object GetService(Type serviceType)
            {
                if (serviceType == null)
                {
                    throw Error.ArgumentNull("serviceType");
                }
                if (!_serviceTypesSingle.Contains(serviceType))
                {
                    throw Error.Argument("serviceType", SRResources.DefaultServices_InvalidServiceType, serviceType.Name);
                }
    
                // Invalidate the cache if the dependency scope has switched
                if (_lastKnownDependencyResolver != _configuration.DependencyResolver)
                {
                    ResetCache();
                }
    
                object result;
    
                _cacheLock.EnterReadLock();
                try
                {
                    if (_cacheSingle.TryGetValue(serviceType, out result))
                    {
                        return result;
                    }
                }
                finally
                {
                    _cacheLock.ExitReadLock();
                }
    
                // Get the service from DI, outside of the lock. If we're coming up hot, this might
                // mean we end up creating the service more than once.
                object dependencyService = _configuration.DependencyResolver.GetService(serviceType);
    
                _cacheLock.EnterWriteLock();
                try
                {
                    if (!_cacheSingle.TryGetValue(serviceType, out result))
                    {
                        result = dependencyService ?? _defaultServicesSingle[serviceType];
                        _cacheSingle[serviceType] = result;
                    }
    
                    return result;
                }
                finally
                {
                    _cacheLock.ExitWriteLock();
                }
            }
     public override IEnumerable<object> GetServices(Type serviceType)
            {
                if (serviceType == null)
                {
                    throw Error.ArgumentNull("serviceType");
                }
                if (!_serviceTypesMulti.Contains(serviceType))
                {
                    throw Error.Argument("serviceType", SRResources.DefaultServices_InvalidServiceType, serviceType.Name);
                }
    
                // Invalidate the cache if the dependency scope has switched
                if (_lastKnownDependencyResolver != _configuration.DependencyResolver)
                {
                    ResetCache();
                }
    
                object[] result;
    
                _cacheLock.EnterReadLock();
                try
                {
                    if (_cacheMulti.TryGetValue(serviceType, out result))
                    {
                        return result;
                    }
                }
                finally
                {
                    _cacheLock.ExitReadLock();
                }
    
                // Get the service from DI, outside of the lock. If we're coming up hot, this might
                // mean we end up creating the service more than once.
                IEnumerable<object> dependencyServices = _configuration.DependencyResolver.GetServices(serviceType);
    
                _cacheLock.EnterWriteLock();
                try
                {
                    if (!_cacheMulti.TryGetValue(serviceType, out result))
                    {
                        result = dependencyServices.Where(s => s != null)
                                                   .Concat(_defaultServicesMulti[serviceType])
                                                   .ToArray();
                        _cacheMulti[serviceType] = result;
                    }
    
                    return result;
                }
                finally
                {
                    _cacheLock.ExitWriteLock();
                }
            }
    

     在我们的日常开发中会遇到2中情况,一种是一个接口对应着一个具体实现类(Dictionary<Type, object> _defaultServicesSingle),还有一种情况是一个接口对应着多个实现类(Dictionary<Type, List<object>> _defaultServicesMulti),所以这里的DefaultServices实际就是把一个接口和一个或则多个实例关联起来。

    现在我们来看看ServicesContainer的GetHostBufferPolicySelector()方法实现,

            public static IHostBufferPolicySelector GetHostBufferPolicySelector(this ServicesContainer services)
            {
                return services.GetService<IHostBufferPolicySelector>();
            }
           private static TService GetService<TService>(this ServicesContainer services)
            {
                if (services == null)
                {
                    throw Error.ArgumentNull("services");
                }

                return (TService)services.GetService(typeof(TService));

            }

    这里其实还是调用的是DefaultServices的GetService方法,这里的IHostBufferPolicySelector接口 和实例是一一对应的,首先在_cacheSingle中根据type来查找实例,如果找到这直接返回,如果没有找到就根 据 _configuration.DependencyResolver.GetService(serviceType)方法来找实例,如果 _cacheSingle key中不含type,这把该type和实例添加到_cacheSingle中来,

     if (!_cacheSingle.TryGetValue(serviceType, out result))
                    {
                        result = dependencyService ?? _defaultServicesSingle[serviceType];
                        _cacheSingle[serviceType] = result;

                    }

    如果前面的 _configuration.DependencyResolver.GetService(serviceType)方法返回null,这里就用默认的_defaultServicesSingle中type对应的实例。

    而HttpConfiguration的DependencyResolver属性如下:

    private IDependencyResolver _dependencyResolver = EmptyResolver.Instance;

      public IDependencyResolver DependencyResolver
            {
                get { return _dependencyResolver; }
                set
                {
                    if (value == null)
                    {
                        throw Error.PropertyNull();
                    }

                    _dependencyResolver = value;
                }
            }
    看见HttpConfiguration的DependencyResolver默认是EmptyResolver.Instance,其GetService实现如下:

      public object GetService(Type serviceType)
            {
                return null;
            }

    到这里我们的_bufferPolicySelector的创建就说完了。DependencyResolver这个东东在mvc里面也有,微软做 这个东东,无非就是让我们在需要的时候设置自己的类型实例。可能是自己接触面比较窄,在工作中我还没遇到要设置DependencyResolver这个 属性的需求。

    下面我们来看看添加header时候用到一个  AddHeaderToHttpRequestMessage(request, headerName, values);方法,主要的实现代码如下:

      private static void AddHeaderToHttpRequestMessage(HttpRequestMessage httpRequestMessage, string headerName, string[] headerValues)
            {
                if (!httpRequestMessage.Headers.TryAddWithoutValidation(headerName, headerValues))
                {
                    httpRequestMessage.Content.Headers.TryAddWithoutValidation(headerName, headerValues);
                }
            }

    这里的HttpRequestHeaders、HttpContentHeaders 都继承于HttpHeaders,TryAddWithoutValidation的实现在HttpHeaders类中


    public bool TryAddWithoutValidation(string name, IEnumerable<string> values)
    {
        if (values == null)
        {
            throw new ArgumentNullException("values");
        }
        if (!this.TryCheckHeaderName(name))
        {
            return false;
        }
        HeaderStoreItemInfo orCreateHeaderInfo = this.GetOrCreateHeaderInfo(name, false);
        foreach (string str in values)
        {
            AddValue(orCreateHeaderInfo, str ?? string.Empty, StoreLocation.Raw);
        }

        return true;
    }
    这里的具体是怎么添加的我们也就忽略它吧。
    到这里我想我们大家对HttpRequestMessage的创建应该比较清楚了吧,直接实例 化   HttpRequestMessage request = new HttpRequestMessage(method, uri);,然后主要设置它的Content属性和添加Content.Headers成员

  • 相关阅读:
    traceroute工作原理
    Android 关于资源适配
    JavaScript对象
    八大排序算法总结
    Linux pipe函数
    cocos2d-x读取xml(适用于cocos2d-x 2.0以上版本号)
    WebService 设计总结
    select poll使用
    QQ强制视频聊天
    图解iPhone开发新手教程
  • 原文地址:https://www.cnblogs.com/majiang/p/2799704.html
Copyright © 2011-2022 走看看