.NET Core HttpClient源码探究
前言
????在之前的文章我們介紹過HttpClient相關(guān)的服務發(fā)現(xiàn),確實HttpClient是目前.NET Core進行Http網(wǎng)絡編程的的主要手段。在之前的介紹中也看到了,我們使用了一個很重要的抽象HttpMessageHandler,接下來我們就探究一下HttpClient源碼,并找尋它和HttpMessageHandler的關(guān)系究竟是怎么樣的。
HttpClient源碼解析
????首先我們找到HttpClient源碼的位置,微軟也提供了專門的網(wǎng)站可以查找.Net Core源碼有興趣的同學可以自行查閱。接下來我們查閱一下HttpClient的核心代碼。首先,我們可以看到HttpClient繼承自HttpMessageInvoker這個類,待會我們在探究這個類。
public class HttpClient : HttpMessageInvoker { }然后我們看下幾個核心的構(gòu)造函數(shù)
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(); }通過這幾個構(gòu)造函數(shù)我們看出,我們可以傳遞自定義的HttpMessageHandler。我們再看無參默認的構(gòu)造,其實也是實例化了HttpClientHandler傳遞給了自己的另一個構(gòu)造函數(shù),我們之前講解過HttpClientHandler是繼承自了HttpMessageHandler,通過最后一個構(gòu)造函數(shù)可知最終HttpMessageHandler,傳給了父類HttpMessageInvoker。到了這里我們基本上就可以感受到HttpMessageHandler在HttpClient中存在的意義。
????接下來,我們從一個最簡單,而且最常用的方法為入口開始探索HttpClient的工作原理。這種方式可能是我們最常用而且最有效的的探索源碼的方式了。個人建議沒看過源碼,或者剛開始入門看源碼的小伙伴們,找源碼的入口一定是你最有把握的的一個,然后逐步深入了解。接下來我們選用HttpClient的GetAsync開始入手,而且是只傳遞Url的那一個。
通過這里我們可以大致了解到。其實大部分最簡單的調(diào)用方式,往往都是從最復雜的調(diào)用方式,一步步的封裝起來的,只是系統(tǒng)幫我們初始化了一部分參數(shù),讓我們按需使用。順著方法一直向下找,最后找到了這里。
public Task<HttpResponseMessage> GetAsync(Uri? requestUri, HttpCompletionOption completionOption,CancellationToken cancellationToken) {return SendAsync(CreateRequestMessage(HttpMethod.Get, requestUri), completionOption, cancellationToken); }由此可以看出這里是所有GetAsync方法的執(zhí)行入口,我們通過查找SendAsync引用可以發(fā)現(xiàn)。不僅僅是GetAsync, PostAsync,PutAsync,DeleteAsync最終都是調(diào)用了這個方法。也就是說SendAsync是所有發(fā)送請求的真正執(zhí)行者。接下來我們就查看SendAsync方法,部分邊角料代碼我粘貼的時候?qū)鰟h減。
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption,CancellationToken cancellationToken) {if (request == null){throw new ArgumentNullException(nameof(request));}CheckDisposed();CheckRequestMessage(request);SetOperationStarted();//這里會把發(fā)送請求的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{//***這里是核心,最終執(zhí)行調(diào)用的地方!!!sendTask = base.SendAsync(request, cts.Token);}catch (Exception e){HandleFinishSendAsyncCleanup(cts, disposeCts);if (e is OperationCanceledException operationException && TimeoutFired(cancellationToken, timeoutTime)){throw CreateTimeoutException(operationException);}throw;}//這里處理輸出的唯一類型HttpResponseMessagereturn 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類中最終執(zhí)行的是父類的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調(diào)用的本質(zhì),其實發(fā)送請求的根本是HttpMessageHandler的SendAsyncTask<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方法,這里基本上可以總結(jié)一點,負責調(diào)用輸入輸出的類只有兩個。一個是提供請求參數(shù)的HttpRequestMessage,另一個是接收輸出的HttpResponseMessage。這里也給我們?nèi)粘9ぷ骶幋a中提供了一個很好的思路。針對具體某個功能的操作方法,最好只保留一個,其外圍調(diào)用,都是基于該方法的封裝。然后我們找到了發(fā)送請求的地方_handler.SendAsync(request, cancellationToken),而handler正是我們通過HttpClient傳遞下來的HttpMessageHandler.由此可知,HttpClient的本質(zhì)是HttpMessageHandler的包裝類。
自定義HttpClient
????探究到這里我們也差不多大概了解到HttpClient類的本質(zhì)是什么了。其實到這里我們可以借助HttpMessageHandler的相關(guān)子類,封裝一個簡單的Http請求類.接下來我將動手實現(xiàn)一個簡單的Http請求類,我們定義一個類叫MyHttpClient,實現(xiàn)代碼如下
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非子類無法直接調(diào)用,所以我封裝了一個MyHttpClientHandler繼承自HttpClientHandler在MyHttpClient中調(diào)用,具體實現(xiàn)如下
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);}} }到這里自己實現(xiàn)MyHttpClient差不多到此結(jié)束了,因為只是講解大致思路,所以方法封裝的相對簡單,只是封裝了Get和Post相關(guān)的方法。
總結(jié)
????通過本文分析HttpClient的源碼,我們大概知道了HttpClient本質(zhì)還是HttpMessageHandler的包裝類。最終的發(fā)送還是調(diào)用的HttpMessageHandler的SendAsync方法。最后,我根據(jù)HttpClientHandler實現(xiàn)了一個MyHttpClient。以上只是本人理解,如果處在理解不正確或者不恰當?shù)牡胤?#xff0c;望多多包涵,同時也期望能指出理解不周的地方。我寫文章的主要一部分是想把我的理解傳遞給大家,歡迎大家多多交流。
????歡迎掃碼關(guān)注????
總結(jié)
以上是生活随笔為你收集整理的.NET Core HttpClient源码探究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Student.Achieve] 学生
- 下一篇: 《Unit Testing》1.1 -1