zoukankan      html  css  js  c++  java
  • .NET Core-HttpClient源码探究【转】

    前言

    在之前的文章我们介绍过HttpClient相关的服务发现,确实HttpClient是目前.NET Core进行Http网络编程的的主要手段。在之前的介绍中也看到了,我们使用了一个很重要的抽象HttpMessageHandler,接下来我们就探究一下HttpClient源码,并找寻它和HttpMessageHandler的关系究竟是怎么样的。

    HttpClient源码解析

    首先我们找到HttpClient源码的位置,微软也提供了专门的网站可以查找.Net Core源码有兴趣的同学可以自行查阅。接下来我们查阅一下HttpClient的核心代码。首先,我们可以看到HttpClient继承自HttpMessageInvoker这个类,待会我们在探究这个类。

    public class HttpClient : HttpMessageInvoker
    {
    }
    

    然后我们看下几个核心的构造函数

    public HttpClient()
        : this(new HttpClientHandler())
    {
    }
    
    public HttpClient(HttpMessageHandler handler)
        : this(handler, true)
    {
    }
    
    public HttpClient(HttpMessageHandler handler, bool disposeHandler)
        : base(handler, disposeHandler)
    {
        _timeout = s_defaultTimeout;
        _maxResponseContentBufferSize = HttpContent.MaxBufferSize;
        _pendingRequestsCts = new CancellationTokenSource();
    }
    

    通过这几个构造函数我们看出,我们可以传递自定义的HttpMessageHandler。我们再看无参默认的构造,其实也是实例化了HttpClientHandler传递给了自己的另一个构造函数,我们之前讲解过HttpClientHandler是继承自了HttpMessageHandler,通过最后一个构造函数可知最终HttpMessageHandler,传给了父类HttpMessageInvoker。到了这里我们基本上就可以感受到HttpMessageHandler在HttpClient中存在的意义。
    接下来,我们从一个最简单,而且最常用的方法为入口开始探索HttpClient的工作原理。这种方式可能是我们最常用而且最有效的的探索源码的方式了。个人建议没看过源码,或者刚开始入门看源码的小伙伴们,找源码的入口一定是你最有把握的的一个,然后逐步深入了解。接下来我们选用HttpClient的GetAsync开始入手,而且是只传递Url的那一个。

    public Task<HttpResponseMessage> GetAsync(string? requestUri)
    {
        return GetAsync(CreateUri(requestUri));
    }
    
    public Task<HttpResponseMessage> GetAsync(Uri? requestUri)
    {
        return GetAsync(requestUri, defaultCompletionOption);
    }
    

    通过这里我们可以大致了解到。其实大部分最简单的调用方式,往往都是从最复杂的调用方式,一步步的封装起来的,只是系统帮我们初始化了一部分参数,让我们按需使用。顺着方法一直向下找,最后找到了这里。

    public Task<HttpResponseMessage> GetAsync(Uri? requestUri, HttpCompletionOption completionOption,
                CancellationToken cancellationToken)
    {
        return SendAsync(CreateRequestMessage(HttpMethod.Get, requestUri), completionOption, cancellationToken);
    }
    

    由此可以看出这里是所有GetAsync方法的执行入口,我们通过查找SendAsync引用可以发现。不仅仅是GetAsync, PostAsync,PutAsync,DeleteAsync最终都是调用了这个方法。也就是说SendAsync是所有发送请求的真正执行者。接下来我们就查看SendAsync方法,部分边角料代码我粘贴的时候将会做删减。

    public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption,
                CancellationToken cancellationToken)
    {
        if (request == null)
        {
            throw new ArgumentNullException(nameof(request));
        }
        CheckDisposed();
        CheckRequestMessage(request);
    
        SetOperationStarted();
        //这里会把发送请求的HttpRequestMessage准备妥当
        PrepareRequestMessage(request);
    
        CancellationTokenSource cts;
        bool disposeCts;
        bool hasTimeout = _timeout != s_infiniteTimeout;
        long timeoutTime = long.MaxValue;
        if (hasTimeout || cancellationToken.CanBeCanceled)
        {
            disposeCts = true;
            cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _pendingRequestsCts.Token);
            if (hasTimeout)
            {
                timeoutTime = Environment.TickCount64 + (_timeout.Ticks / TimeSpan.TicksPerMillisecond);
                cts.CancelAfter(_timeout);
            }
        }
        else
        {
            disposeCts = false;
            cts = _pendingRequestsCts;
        }
        Task<HttpResponseMessage> sendTask;
        try
        {
            //***这里是核心,最终执行调用的地方!!!
            sendTask = base.SendAsync(request, cts.Token);
        }
        catch (Exception e)
        {
            HandleFinishSendAsyncCleanup(cts, disposeCts);
            if (e is OperationCanceledException operationException && TimeoutFired(cancellationToken, timeoutTime))
            {
                throw CreateTimeoutException(operationException);
            }
            throw;
        }
        //这里处理输出的唯一类型HttpResponseMessage
        return completionOption == HttpCompletionOption.ResponseContentRead && !string.Equals(request.Method.Method, "HEAD", StringComparison.OrdinalIgnoreCase) ?
            FinishSendAsyncBuffered(sendTask, request, cts, disposeCts, cancellationToken, timeoutTime) :
            FinishSendAsyncUnbuffered(sendTask, request, cts, disposeCts, cancellationToken, timeoutTime);
    }
    

    通过分析这段代码可以得知,HttpClient类中最终执行的是父类的SendAsync的方法。看来是时候查看父类HttpMessageInvoker的源码了。

    HttpMessageInvoker源码解析

    public class HttpMessageInvoker : IDisposable
    {
        private volatile bool _disposed;
        private readonly bool _disposeHandler;
        private readonly HttpMessageHandler _handler;
    
        public HttpMessageInvoker(HttpMessageHandler handler)
            : this(handler, true)
        {
        }
    
        public HttpMessageInvoker(HttpMessageHandler handler, bool disposeHandler)
        {
            if (NetEventSource.IsEnabled) NetEventSource.Enter(this, handler);
    
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }
    
            if (NetEventSource.IsEnabled) NetEventSource.Associate(this, handler);
    
            _handler = handler;
            _disposeHandler = disposeHandler;
    
            if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
        }
    
        public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            CheckDisposed();
    
            if (NetEventSource.IsEnabled) NetEventSource.Enter(this, request);
    
            //***这里是HttpClient调用的本质,其实发送请求的根本是HttpMessageHandler的SendAsync
            Task<HttpResponseMessage> task = _handler.SendAsync(request, cancellationToken);
    
            if (NetEventSource.IsEnabled) NetEventSource.Exit(this, task);
            return task;
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (disposing && !_disposed)
            {
                _disposed = true;
    
                if (_disposeHandler)
                {
                    _handler.Dispose();
                }
            }
        }
    
        private void CheckDisposed()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(GetType().ToString());
            }
        }
    }
    

    是的,你并没有看错,整个HttpMessageInvoker就这么多代码,而且还是靠子类初始化过来的基本属性。找到SendAsync方法,这里基本上可以总结一点,负责调用输入输出的类只有两个。一个是提供请求参数的HttpRequestMessage,另一个是接收输出的HttpResponseMessage。这里也给我们日常工作编码中提供了一个很好的思路。针对具体某个功能的操作方法,最好只保留一个,其外围调用,都是基于该方法的封装。然后我们找到了发送请求的地方_handler.SendAsync(request, cancellationToken),而handler正是我们通过HttpClient传递下来的HttpMessageHandler.由此可知,HttpClient的本质是HttpMessageHandler的包装类。

    自定义HttpClient

    探究到这里我们也差不多大概了解到HttpClient类的本质是什么了。其实到这里我们可以借助HttpMessageHandler的相关子类,封装一个简单的Http请求类.接下来我将动手实现一个简单的Http请求类,我们定义一个类叫MyHttpClient,实现代码如下

    public class MyHttpClient : IDisposable
    {
        private readonly MyHttpClientHandler _httpClientHandler;
        private readonly bool _disposeHandler;
        private volatile bool _disposed;
    
        public MyHttpClient()
            :this(true)
        {
        }
    
        public MyHttpClient(bool disposeHandler)
        {
            _httpClientHandler = new MyHttpClientHandler();
            _disposeHandler = disposeHandler;
        }
    
        public Task<HttpResponseMessage> GetAsync(string url)
        {
            return GetAsync(new Uri(url));
        }
    
        public Task<HttpResponseMessage> GetAsync(Uri uri)
        {
            HttpRequestMessage httpRequest = new HttpRequestMessage
            {
                Method = HttpMethod.Get,
                RequestUri = uri
            };
            return SendAsync(httpRequest,CancellationToken.None);
        }
    
        public Task<HttpResponseMessage> PostAsync(string url, HttpContent content)
        {
            return PostAsync(new Uri(url),content,null);
        }
    
        public Task<HttpResponseMessage> PostAsync(Uri uri, HttpContent content,Dictionary<string,string> headers)
        {
            HttpRequestMessage httpRequest = new HttpRequestMessage
            {
                Method = HttpMethod.Post,
                RequestUri = uri,
                Content = content
            };
            if (headers != null && headers.Any())
            {
                foreach (var head in headers)
                {
                    httpRequest.Headers.Add(head.Key,head.Value);
                }
            }
            return SendAsync(httpRequest, CancellationToken.None);
        }
    
        private Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
        {
            if (httpRequest.RequestUri == null || string.IsNullOrWhiteSpace(httpRequest.RequestUri.OriginalString))
            {
                throw new ArgumentNullException("RequestUri");
            }
            return _httpClientHandler.SendRequestAsync(httpRequest, cancellationToken);
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (disposing && !_disposed)
            {
                _disposed = true;
    
                if (_disposeHandler)
                {
                    _httpClientHandler.Dispose();
                }
            }
        }
    }
    

    由于HttpMessageHandler的SendAsync是protected非子类无法直接调用,所以我封装了一个MyHttpClientHandler继承自HttpClientHandler在MyHttpClient中调用,具体实现如下

    public class MyHttpClientHandler : HttpClientHandler
    {
        public Task<HttpResponseMessage> SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return this.SendAsync(request, cancellationToken);
        }
    
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return base.SendAsync(request, cancellationToken);
        }
    }
    

    最后写了一段测试代码

    using (MyHttpClient httpClient = new MyHttpClient())
    {
        Task<HttpResponseMessage> httpResponse = httpClient.GetAsync("http://localhost:5000/Person/GetPerson?userId=1");
        HttpResponseMessage responseMessage = httpResponse.Result;
        if (responseMessage.StatusCode == HttpStatusCode.OK)
        {
            string content = responseMessage.Content.ReadAsStringAsync().Result;
            if (!string.IsNullOrWhiteSpace(content))
            {
                System.Console.WriteLine(content);
            }
        }
    }
    

    到这里自己实现MyHttpClient差不多到此结束了,因为只是讲解大致思路,所以方法封装的相对简单,只是封装了Get和Post相关的方法。

    总结

    通过本文分析HttpClient的源码,我们大概知道了HttpClient本质还是HttpMessageHandler的包装类。最终的发送还是调用的HttpMessageHandler的SendAsync方法。最后,我根据HttpClientHandler实现了一个MyHttpClient。

    转:https://www.cnblogs.com/wucy/p/12964869.html

  • 相关阅读:
    checkedListBox 的用发
    C# 控件命名规范
    控件数据及相应的事件处理
    MDI 窗口的创建
    摄像头中运动物体识别
    1
    静态检测大风车初版
    不会难道我还不能附上链接吗
    计算机操作素材
    数字识别
  • 原文地址:https://www.cnblogs.com/fanfan-90/p/15024603.html
Copyright © 2011-2022 走看看