.Net5下WebRequest、WebClient、HttpClient是否還存在使用爭議?

  WebRequest、WebClient、HttpClient 是C#中常用的三個Http請求的類,時不時也會有人發表對這三個類使用場景的總結,本人是HttpClient 一把梭,也沒太關注它們的內部實現,最近比較閑就看了下它們內部實現到底有什麼差別。

  WebClient其實就是對WebRequest的二次封裝,便於使用。所以主要看WebRequest和HttpClient之間的差別。WebRequest的默認實現是HttpWebRequest我把其中關於今天主題關鍵的程式碼貼出來:

  

 1 public override WebResponse GetResponse()
 2         {
 3             try
 4             {
 5                 _sendRequestCts = new CancellationTokenSource();
 6                 return SendRequest(async: false).GetAwaiter().GetResult();
 7             }
 8             catch (Exception ex)
 9             {
10                 throw WebException.CreateCompatibleException(ex);
11             }
12         }

 

 1 private async Task<WebResponse> SendRequest(bool async)
 2         {
 3             if (RequestSubmitted)
 4             {
 5                 throw new InvalidOperationException(SR.net_reqsubmitted);
 6             }
 7 
 8             var request = new HttpRequestMessage(new HttpMethod(_originVerb), _requestUri);
 9 
10             bool disposeRequired = false;
11             HttpClient? client = null;
12             try
13             {
14                 client = GetCachedOrCreateHttpClient(async, out disposeRequired);
15                 if (_requestStream != null)
16                 {
17                     ArraySegment<byte> bytes = _requestStream.GetBuffer();
18                     request.Content = new ByteArrayContent(bytes.Array!, bytes.Offset, bytes.Count);
19                 }
20 
21                 if (_hostUri != null)
22                 {
23                     request.Headers.Host = Host;
24                 }
25 
26                 // Copy the HttpWebRequest request headers from the WebHeaderCollection into HttpRequestMessage.Headers and
27                 // HttpRequestMessage.Content.Headers.
28                 foreach (string headerName in _webHeaderCollection)
29                 {
30                     // The System.Net.Http APIs require HttpRequestMessage headers to be properly divided between the request headers
31                     // collection and the request content headers collection for all well-known header names.  And custom headers
32                     // are only allowed in the request headers collection and not in the request content headers collection.
33                     if (IsWellKnownContentHeader(headerName))
34                     {
35                         if (request.Content == null)
36                         {
37                             // Create empty content so that we can send the entity-body header.
38                             request.Content = new ByteArrayContent(Array.Empty<byte>());
39                         }
40 
41                         request.Content.Headers.TryAddWithoutValidation(headerName, _webHeaderCollection[headerName!]);
42                     }
43                     else
44                     {
45                         request.Headers.TryAddWithoutValidation(headerName, _webHeaderCollection[headerName!]);
46                     }
47                 }
48 
49                 request.Headers.TransferEncodingChunked = SendChunked;
50 
51                 if (KeepAlive)
52                 {
53                     request.Headers.Connection.Add(HttpKnownHeaderNames.KeepAlive);
54                 }
55                 else
56                 {
57                     request.Headers.ConnectionClose = true;
58                 }
59 
60                 request.Version = ProtocolVersion;
61 
62                 _sendRequestTask = async ?
63                     client.SendAsync(request, _allowReadStreamBuffering ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead, _sendRequestCts!.Token) :
64                     Task.FromResult(client.Send(request, _allowReadStreamBuffering ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead, _sendRequestCts!.Token));
65 
66                 HttpResponseMessage responseMessage = await _sendRequestTask.ConfigureAwait(false);
67 
68                 HttpWebResponse response = new HttpWebResponse(responseMessage, _requestUri, _cookieContainer);
69 
70                 int maxSuccessStatusCode = AllowAutoRedirect ? 299 : 399;
71                 if ((int)response.StatusCode > maxSuccessStatusCode || (int)response.StatusCode < 200)
72                 {
73                     throw new WebException(
74                         SR.Format(SR.net_servererror, (int)response.StatusCode, response.StatusDescription),
75                         null,
76                         WebExceptionStatus.ProtocolError,
77                         response);
78                 }
79 
80                 return response;
81             }
82             finally
83             {
84                 if (disposeRequired)
85                 {
86                     client?.Dispose();
87                 }
88             }
89         }

   SendRequest(bool async)方法中調用了一個方法GetCachedOrCreateHttpClient看到此處我一陣感慨,最終還是調用了HttpClient。到此也能粗顯的看出來,三者在.Net5下,本質已經沒有了區別,只是對外的表現形式不同而已,使用了時候不必太糾結內部實現優先不同,只關注自己喜歡怎麼樣的寫法就行了。