Do HttpClient and HttpClientHandler have to be disposed between requests?

前端 未结 12 1957
鱼传尺愫
鱼传尺愫 2020-11-22 02:06

System.Net.Http.HttpClient and System.Net.Http.HttpClientHandler in .NET Framework 4.5 implement IDisposable (via System.Net.Http.HttpMessageInvoker).

The usin

12条回答
  •  既然无缘
    2020-11-22 02:32

    If you want to dispose of HttpClient, you can if you set it up as a resource pool. And at the end of your application, you dispose your resource pool.

    Code:

    // Notice that IDisposable is not implemented here!
    public interface HttpClientHandle
    {
        HttpRequestHeaders DefaultRequestHeaders { get; }
        Uri BaseAddress { get; set; }
        // ...
        // All the other methods from peeking at HttpClient
    }
    
    public class HttpClientHander : HttpClient, HttpClientHandle, IDisposable
    {
        public static ConditionalWeakTable _httpClientsPool;
        public static HashSet _uris;
    
        static HttpClientHander()
        {
            _httpClientsPool = new ConditionalWeakTable();
            _uris = new HashSet();
            SetupGlobalPoolFinalizer();
        }
    
        private DateTime _delayFinalization = DateTime.MinValue;
        private bool _isDisposed = false;
    
        public static HttpClientHandle GetHttpClientHandle(Uri baseUrl)
        {
            HttpClientHander httpClient = _httpClientsPool.GetOrCreateValue(baseUrl);
            _uris.Add(baseUrl);
            httpClient._delayFinalization = DateTime.MinValue;
            httpClient.BaseAddress = baseUrl;
    
            return httpClient;
        }
    
        void IDisposable.Dispose()
        {
            _isDisposed = true;
            GC.SuppressFinalize(this);
    
            base.Dispose();
        }
    
        ~HttpClientHander()
        {
            if (_delayFinalization == DateTime.MinValue)
                _delayFinalization = DateTime.UtcNow;
            if (DateTime.UtcNow.Subtract(_delayFinalization) < base.Timeout)
                GC.ReRegisterForFinalize(this);
        }
    
        private static void SetupGlobalPoolFinalizer()
        {
            AppDomain.CurrentDomain.ProcessExit +=
                (sender, eventArgs) => { FinalizeGlobalPool(); };
        }
    
        private static void FinalizeGlobalPool()
        {
            foreach (var key in _uris)
            {
                HttpClientHander value = null;
                if (_httpClientsPool.TryGetValue(key, out value))
                    try { value.Dispose(); } catch { }
            }
    
            _uris.Clear();
            _httpClientsPool = null;
        }
    }
    

    var handler = HttpClientHander.GetHttpClientHandle(new Uri("base url")).

    • HttpClient, as an interface, can't call Dispose().
    • Dispose() will be called in a delayed fashion by the Garbage Collector. Or when the program cleans up the object through its destructor.
    • Uses Weak References + delayed cleanup logic so it remains in use so long as it is being reused frequently.
    • It only allocates a new HttpClient for each base URL passed to it. Reasons explained by Ohad Schneider answer below. Bad behavior when changing base url.
    • HttpClientHandle allows for Mocking in tests

提交回复
热议问题