.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下,本质已经没有了区别,只是对外的表现形式不同而已,使用了时候不必太纠结内部实现优先不同,只关注自己喜欢怎么样的写法就行了。