本文旨在引導開發人員完成從 HttpWebRequest、ServicePoint,以及 ServicePointManager 移轉到 HttpClient 的程序。 由於舊版 API 已過時,而 HttpClient 又提供了許多優點,包括效能提升、資源管理更完善,以及 API 設計更現代化、更靈活,因此移轉是必要的。 透過遵循本文件所概述的步驟,開發人員將能順利轉換其代碼庫,並充分利用 HttpClient 所提供的功能。
警告
從 HttpWebRequest、 ServicePoint和 ServicePointManager 移轉至 HttpClient 不只是「好」效能改善。 重要的是要了解,您移至 .NET (Core) 後,現有 WebRequest 邏輯的效能很可能會大幅降低。 這是因為 WebRequest 維持為最小相容性層,這意味著它缺乏許多最佳化,例如在許多情況下的連線重複使用。 因此,轉換到 HttpClient 對於確保您應用程式的效能和資源管理符合現代標準是非常重要的。
從 HttpWebRequest 移轉至 HttpClient
讓我們從撰寫一些範例開始:
使用 HttpWebRequest 進行簡單的 GET 要求
以下是程式碼外觀的範例:
HttpWebRequest request = WebRequest.CreateHttp(uri);
using WebResponse response = await request.GetResponseAsync();
使用 HttpClient 進行簡單的 GET 要求
以下是程式碼外觀的範例:
HttpClient client = new();
using HttpResponseMessage message = await client.GetAsync(uri);
使用 HttpWebRequest 進行簡單的 POST 要求
以下是程式碼外觀的範例:
HttpWebRequest request = WebRequest.CreateHttp(uri);
request.Method = "POST";
request.ContentType = "text/plain";
await using Stream stream = await request.GetRequestStreamAsync();
await stream.WriteAsync("Hello World!"u8.ToArray());
using WebResponse response = await request.GetResponseAsync();
使用 HttpClient 進行簡單的 POST 要求
以下是程式碼外觀的範例:
HttpClient client = new();
using HttpResponseMessage responseMessage = await client.PostAsync(uri, new StringContent("Hello World!"));
HttpWebRequest 至 HttpClient、 SocketsHttpHandler 遷移指南
| HttpWebRequest原始 API | 新增 API | 備註 |
|---|---|---|
Accept |
Accept | 範例:設定請求標題。 |
Address |
RequestUri | 範例:擷取重新導向的 URI |
AllowAutoRedirect |
AllowAutoRedirect | 範例:設定 SocketsHttpHandler 屬性。 |
AllowReadStreamBuffering |
沒有直接的對等 API | 緩衝屬性的使用方式。 |
AllowWriteStreamBuffering |
沒有直接的對等 API | 緩衝屬性的使用方式。 |
AuthenticationLevel |
沒有直接的對等 API | 範例:啟用相互驗證。 |
AutomaticDecompression |
AutomaticDecompression | 範例:設定 SocketsHttpHandler 屬性。 |
CachePolicy |
沒有直接的對等 API | 範例:套用 CachePolicy 標題。 |
ClientCertificates |
SslOptions.ClientCertificates | HttpClient 中憑證相關屬性的使用方式。 |
Connection |
Connection | 範例:設定請求標題。 |
ConnectionGroupName |
沒有同等項目 API | 沒有因應措施 |
ContentLength |
ContentLength | 範例:設定內容標題。 |
ContentType |
ContentType | 範例:設定內容標題。 |
ContinueDelegate |
沒有同等項目 API | 沒有因應措施。 |
ContinueTimeout |
Expect100ContinueTimeout | 範例:設定 SocketsHttpHandler 屬性。 |
CookieContainer |
CookieContainer | 範例:設定 SocketsHttpHandler 屬性。 |
Credentials |
Credentials | 範例:設定 SocketsHttpHandler 屬性。 |
Date |
Date | 範例:設定請求標題。 |
DefaultCachePolicy |
沒有直接的對等 API | 範例:套用 CachePolicy 標題。 |
DefaultMaximumErrorResponseLength |
沒有直接的對等 API | 範例:在 HttpClient 中設定 MaximumErrorResponseLength。 |
DefaultMaximumResponseHeadersLength |
沒有同等項目 API | 可以使用 MaxResponseHeadersLength 代替。 |
DefaultWebProxy |
沒有同等項目 API | 可以使用 Proxy 代替。 |
Expect |
Expect | 範例:設定請求標題。 |
HaveResponse |
沒有同等項目 API | 具有 HttpResponseMessage 執行個體。 |
Headers |
Headers | 範例:設定請求標題。 |
Host |
Host | 範例:設定請求標題。 |
IfModifiedSince |
IfModifiedSince | 範例:設定請求標題。 |
ImpersonationLevel |
沒有直接的對等 API | 範例:變更 ImpersonationLevel。 |
KeepAlive |
沒有直接的對等 API | 範例:設定請求標題。 |
MaximumAutomaticRedirections |
MaxAutomaticRedirections | 範例:設定 SocketsHttpHandler 屬性。 |
MaximumResponseHeadersLength |
MaxResponseHeadersLength | 範例:設定 SocketsHttpHandler 屬性。 |
MediaType |
沒有直接的對等 API | 範例:設定內容標題。 |
Method |
Method | 範例:HttpRequestMessage 屬性的使用方式。 |
Pipelined |
沒有同等項目 API |
HttpClient 不支援管線傳遞。 |
PreAuthenticate |
PreAuthenticate | |
ProtocolVersion |
HttpRequestMessage.Version |
範例:HttpRequestMessage 屬性的使用方式。 |
Proxy |
Proxy | 範例:設定 SocketsHttpHandler 屬性。 |
ReadWriteTimeout |
沒有直接的對等 API | SocketsHttpHandler 和 ConnectCallback 的使用方式。 |
Referer |
Referrer | 範例:設定請求標題。 |
RequestUri |
RequestUri | 範例:HttpRequestMessage 屬性的使用方式。 |
SendChunked |
TransferEncodingChunked | 範例:設定請求標題。 |
ServerCertificateValidationCallback |
SslOptions.RemoteCertificateValidationCallback | 範例:設定 SocketsHttpHandler 屬性。 |
ServicePoint |
沒有同等項目 API |
ServicePoint 不是 HttpClient 其中的一部分。 |
SupportsCookieContainer |
沒有同等項目 API | 這對 true 來說永遠是 HttpClient。 |
Timeout |
Timeout | |
TransferEncoding |
TransferEncoding | 範例:設定請求標題。 |
UnsafeAuthenticatedConnectionSharing |
沒有同等項目 API | 沒有因應措施 |
UseDefaultCredentials |
沒有直接的對等 API | 範例:設定 SocketsHttpHandler 屬性。 |
UserAgent |
UserAgent | 範例:設定請求標題。 |
移轉 ServicePointManager 的使用方式
您應該注意,ServicePointManager 是靜態類別,這表示對其屬性所做的任何變更,都會對應用程式中所有新建立的 ServicePoint 物件產生全域影響。 例如,當您修改ConnectionLimit或Expect100Continue等屬性時,它會影響每個新ServicePoint實例。
警告
在現代的 .NET 中,HttpClient 不會考慮在 ServicePointManager 上設定的任何組態。
ServicePointManager屬性對應
| ServicePointManager原始 API | 新增 API | 備註 |
|---|---|---|
CheckCertificateRevocationList |
SslOptions.CertificateRevocationCheckMode | 範例:使用 SocketsHttpHandler 啟用 CRL 檢查。 |
DefaultConnectionLimit |
MaxConnectionsPerServer | 範例:設定 SocketsHttpHandler 屬性。 |
DnsRefreshTimeout |
沒有同等項目 API | 範例:啟用 Dns 循環配置資源。 |
EnableDnsRoundRobin |
沒有同等項目 API | 範例:啟用 Dns 循環配置資源。 |
EncryptionPolicy |
SslOptions.EncryptionPolicy | 範例:設定 SocketsHttpHandler 屬性。 |
Expect100Continue |
ExpectContinue | 範例:設定請求標題。 |
MaxServicePointIdleTime |
PooledConnectionIdleTimeout | 範例:設定 SocketsHttpHandler 屬性。 |
MaxServicePoints |
沒有同等項目 API |
ServicePoint 不是 HttpClient 其中的一部分。 |
ReusePort |
沒有直接的對等 API | SocketsHttpHandler 和 ConnectCallback 的使用方式。 |
SecurityProtocol |
SslOptions.EnabledSslProtocols | 範例:設定 SocketsHttpHandler 屬性。 |
ServerCertificateValidationCallback |
SslOptions.RemoteCertificateValidationCallback | 兩者都是 RemoteCertificateValidationCallback |
UseNagleAlgorithm |
沒有直接的對等 API | SocketsHttpHandler 和 ConnectCallback 的使用方式。 |
警告
在現代的 .NET 中,UseNagleAlgorithm 和 Expect100Continue 屬性的預設值設定為 false。 這些值在 .NET Framework 中預設為 true。
ServicePointManager 方法對應
| ServicePointManager原始 API | 新增 API | 備註 |
|---|---|---|
FindServicePoint |
沒有同等項目 API | 沒有因應措施 |
SetTcpKeepAlive |
沒有直接的對等 API | SocketsHttpHandler 和 ConnectCallback 的使用方式。 |
ServicePoint屬性對應
| ServicePoint原始 API | 新增 API | 備註 |
|---|---|---|
Address |
HttpRequestMessage.RequestUri |
這是要求 URI,您可以在 HttpRequestMessage 下找到這項資訊。 |
BindIPEndPointDelegate |
沒有直接的對等 API | SocketsHttpHandler 和 ConnectCallback 的使用方式。 |
Certificate |
沒有直接的對等 API | 此資訊可以從 RemoteCertificateValidationCallback 擷取。
範例:擷取憑證 |
ClientCertificate |
沒有同等項目 API | 範例:啟用相互驗證。 |
ConnectionLeaseTimeout |
SocketsHttpHandler.PooledConnectionLifetime |
HttpClient 中的對等設定 |
ConnectionLimit |
MaxConnectionsPerServer | 範例:設定 SocketsHttpHandler 屬性。 |
ConnectionName |
沒有同等項目 API | 沒有因應措施 |
CurrentConnections |
沒有同等項目 API | 請參閱 .NET 中的網路遙測。 |
Expect100Continue |
ExpectContinue | 範例:設定請求標題。 |
IdleSince |
沒有同等項目 API | 沒有因應措施 |
MaxIdleTime |
PooledConnectionIdleTimeout | 範例:設定 SocketsHttpHandler 屬性。 |
ProtocolVersion |
HttpRequestMessage.Version |
範例:HttpRequestMessage 屬性的使用方式。 |
ReceiveBufferSize |
沒有直接的對等 API | SocketsHttpHandler 和 ConnectCallback 的使用方式。 |
SupportsPipelining |
沒有同等項目 API |
HttpClient 不支援管線傳遞。 |
UseNagleAlgorithm |
沒有直接的對等 API | SocketsHttpHandler 和 ConnectCallback 的使用方式。 |
ServicePoint 方法對應
| ServicePoint原始 API | 新增 API | 備註 |
|---|---|---|
CloseConnectionGroup |
沒有對等項目 | 沒有因應措施 |
SetTcpKeepAlive |
沒有直接的對等 API | SocketsHttpHandler 和 ConnectCallback 的使用方式。 |
HttpClient 和 HttpRequestMessage 屬性的使用
在 .NET 中使用 HttpClient 時,您可以存取各種屬性,這些屬性可讓您設定和自訂 HTTP 要求與回應。 了解這些屬性可以幫助您充分利用 HttpClient 並確保您的應用程式與 Web 服務高效、安全地通訊。
範例:HttpRequestMessage 屬性的使用方式
以下是如何使用 HttpClient 和 HttpRequestMessage 一起使用的範例:
var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://example.com"); // Method and RequestUri usage
using var request = new HttpRequestMessage() // Alternative way to set RequestUri and Method
{
RequestUri = new Uri("https://example.com"),
Method = HttpMethod.Post
};
request.Headers.Add("Custom-Header", "value");
request.Content = new StringContent("somestring");
using var response = await client.SendAsync(request);
var protocolVersion = response.RequestMessage.Version; // Fetch `ProtocolVersion`.
範例:擷取重新導向的 URI
以下是如何取得重新導向 URI 的範例 (與 HttpWebRequest.Address 相同):
var client = new HttpClient();
using var response = await client.GetAsync(uri);
var redirectedUri = response.RequestMessage.RequestUri;
SocketsHttpHandler 和 ConnectCallback 的用法
ConnectCallback 中的 SocketsHttpHandler 屬性允許開發人員自訂建立 TCP 連線的程序。 這對於需要控制 DNS 解析或在連線上套用特定通訊端選項的情況非常有用。 透過使用 ConnectCallback,您可以在 HttpClient 使用之前截取並修改連線程序。
範例:將 IP 位址繫結至通訊端
在使用 HttpWebRequest 的舊方法中,您可能會使用自訂邏輯將特定 IP 位址綁定到通訊端。 以下是使用 HttpClient 和 ConnectCallback 實現類似功能的方法:
使用HttpWebRequest的舊程式代碼:
HttpWebRequest request = WebRequest.CreateHttp(uri);
request.ServicePoint.BindIPEndPointDelegate = (servicePoint, remoteEndPoint, retryCount) =>
{
// Bind to a specific IP address
IPAddress localAddress = IPAddress.Parse("192.168.1.100");
return new IPEndPoint(localAddress, 0);
};
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
使用 HttpClient 與ConnectCallback的新程式碼:
var handler = new SocketsHttpHandler
{
ConnectCallback = async (context, cancellationToken) =>
{
// Bind to a specific IP address
IPAddress localAddress = IPAddress.Parse("192.168.1.100");
var socket = new Socket(localAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Bind(new IPEndPoint(localAddress, 0));
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
return new NetworkStream(socket, ownsSocket: true);
}
catch
{
socket.Dispose();
throw;
}
}
};
var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);
範例:套用特定的通訊端選項
如果您需要應用特定的通訊端選項,例如啟用 TCP 保持運作,您可以在 ConnectCallback 使用套接字之前使用 HttpClient 來設定通訊端。 事實上,ConnectCallback 設定通訊端選項更有彈性。
使用HttpWebRequest的舊程式代碼:
ServicePointManager.ReusePort = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
request.ServicePoint.SetTcpKeepAlive(true, 60000, 1000);
request.ServicePoint.ReceiveBufferSize = 8192;
request.ServicePoint.UseNagleAlgorithm = false;
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
使用 HttpClient 與ConnectCallback的新程式碼:
var handler = new SocketsHttpHandler
{
ConnectCallback = async (context, cancellationToken) =>
{
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
try
{
// Setting TCP Keep Alive
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 60);
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 1);
// Setting ReceiveBufferSize
socket.ReceiveBufferSize = 8192;
// Enabling ReusePort
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, true);
// Disabling Nagle Algorithm
socket.NoDelay = true;
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
return new NetworkStream(socket, ownsSocket: true);
}
catch
{
socket.Dispose();
throw;
}
}
};
var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);
範例:啟用 DNS 循環配置資源
DNS 循環配置資源是一種技術,用來透過與單一網域名相關聯的 IP 位址清單,將網路流量分散到多部伺服器。 這有助於進行負載平衡,並改善服務的可用性。 使用 HttpClient時,您可以手動處理 DNS 解析,並透過SocketsHttpHandler的ConnectCallback屬性循環切換 IP 位址,以實作 DNS 循環配置。
若要啟用 HttpClientDNS 循環,您可以使用 ConnectCallback 屬性手動解析 DNS 紀錄並循環 IP 位址。 以下是 HttpWebRequest 和 HttpClient 的範例:
使用HttpWebRequest的舊程式代碼:
ServicePointManager.DnsRefreshTimeout = 60000;
ServicePointManager.EnableDnsRoundRobin = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
在較舊的 HttpWebRequest API 中,由於此功能的內建支援,因此啟用 DNS 循環配置資源相當簡單。 不過,較新 HttpClient API 不提供相同的內建功能。 儘管如此,您仍可以透過實施 DnsRoundRobinConnector 來實現類似行為,這會手動輪替 DNS 解析所傳回的 IP 位址。
使用 HttpClient的新程式代碼:
// This is available as NuGet Package: https://www.nuget.org/packages/DnsRoundRobin/
// The original source code can be found also here: https://github.com/MihaZupan/DnsRoundRobin
public sealed class DnsRoundRobinConnector : IDisposable
如需DnsRoundRobinConnector的實施,請參閱DnsRoundRobin.cs。
DnsRoundRobinConnector 使用方式:
private static readonly DnsRoundRobinConnector s_roundRobinConnector = new(
dnsRefreshInterval: TimeSpan.FromSeconds(10),
endpointConnectTimeout: TimeSpan.FromSeconds(5));
static async Task DnsRoundRobinConnectAsync()
{
var handler = new SocketsHttpHandler
{
ConnectCallback = async (context, cancellation) =>
{
Socket socket = await DnsRoundRobinConnector.Shared.ConnectAsync(context.DnsEndPoint, cancellation);
// Or you can create and use your custom DnsRoundRobinConnector instance
// Socket socket = await s_roundRobinConnector.ConnectAsync(context.DnsEndPoint, cancellation);
return new NetworkStream(socket, ownsSocket: true);
}
};
var client = new HttpClient(handler);
HttpResponseMessage response = await client.GetAsync(Uri);
}
範例:設定 SocketsHttpHandler 屬性
SocketsHttpHandler 是 .NET 中功能強大且靈活的處理程序,它提供了用於管理 HTTP 連線的進階組態選項。 透過設定 的 SocketsHttpHandler各種屬性,您可以微調 HTTP 用戶端的行為以滿足特定要求,例如效能最佳化、安全性增強和自訂連線處理。
以下是如何使用各種屬性進行配置 SocketsHttpHandler 並將其與以下一起使用 HttpClient的範例:
var cookieContainer = new CookieContainer();
cookieContainer.Add(new Cookie("cookieName", "cookieValue"));
var handler = new SocketsHttpHandler
{
AllowAutoRedirect = true,
AutomaticDecompression = DecompressionMethods.All,
Expect100ContinueTimeout = TimeSpan.FromSeconds(1),
CookieContainer = cookieContainer,
Credentials = new NetworkCredential("user", "pass"),
MaxAutomaticRedirections = 10,
MaxResponseHeadersLength = 1,
Proxy = new WebProxy("http://proxyserver:8080"), // Don't forget to set UseProxy
UseProxy = true,
};
var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);
範例:變更 ImpersonationLevel
此功能僅限於特定平台,而且有些過時。 如果您需要變通方法,可以參考此章節的程式碼。
在HttpClient中使用憑證及 TLS 相關屬性
使用 HttpClient 時,您可能需要處理各種用途的用戶端憑證,例如伺服器憑證的自訂驗證或擷取伺服器證書。
HttpClient 提供數個屬性和選項,可有效地管理憑證。
範例:使用 SocketsHttpHandler 檢查證書撤銷清單
CheckCertificateRevocationList 中的 SocketsHttpHandler.SslOptions 屬性可讓開發人員在 SSL/TLS 交握期間啟用或停用憑證撤銷清單 (CRL) 的檢查。 啟用此屬性可確保用戶端會驗證是否已撤銷伺服器的憑證,進而增強連線的安全性。
使用HttpWebRequest的舊程式代碼:
ServicePointManager.CheckCertificateRevocationList = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
使用 HttpClient的新程式代碼:
bool checkCertificateRevocationList = true;
var handler = new SocketsHttpHandler
{
SslOptions =
{
CertificateRevocationCheckMode = checkCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
}
};
var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);
範例:擷取憑證
若要從 RemoteCertificateValidationCallback 的 HttpClient 取得憑證,您可以使用 ServerCertificateCustomValidationCallback 或 HttpClientHandler 的 SocketsHttpHandler.SslOptions 屬性。 此回呼可讓您在 SSL/TLS 交握期間檢查伺服器的憑證。
使用HttpWebRequest的舊程式代碼:
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
X509Certificate? serverCertificate = request.ServicePoint.Certificate;
使用 HttpClient的新程式代碼:
X509Certificate? serverCertificate = null;
var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
serverCertificate = certificate;
// Leave the validation as-is.
return sslPolicyErrors == SslPolicyErrors.None;
}
}
};
var client = new HttpClient(handler);
using var response = await client.GetAsync("https://example.com");
範例:啟用相互驗證
相互驗證也稱為雙向 SSL 或用戶端憑證驗證,是客戶端和伺服器彼此驗證的安全性程式。 這可確保雙方都是自稱的人,為敏感的通訊提供多一層安全保障。 在 HttpClient 中,您可以透過設定 HttpClientHandler 或 SocketsHttpHandler 以包含用戶端憑證並驗證伺服器的憑證,來啟用相互驗證。
若要啟用相互驗證,請執行以下步驟:
- 載入用戶端憑證。
- 設置
HttpClientHandler或SocketsHttpHandler以包含用戶端憑證。 - 如果需要自訂驗證,請設定伺服器憑證驗證回呼。
以下是使用 SocketsHttpHandler的範例:
var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
ClientCertificates = new X509CertificateCollection
{
// Load the client certificate from a file
new X509Certificate2("path_to_certificate.pfx", "certificate_password")
},
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
// Custom validation logic for the server certificate
return sslPolicyErrors == SslPolicyErrors.None;
}
}
};
var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);
標題屬性的使用方式
標題在 HTTP 通訊中扮演重要角色,提供有關要求和回應的基本中繼資料。 在 .NET 中使用 HttpClient 時,您可以設定和管理各種標題屬性,以控制 HTTP 要求和回應的行為。 了解如何有效地使用這些標題屬性,可協助您確保應用程式能有效率且安全地與 Web 服務通訊。
設定請求標題
請求標題可用來向伺服器提供有關所提出要求的其他資訊。 常見的使用案例包括指定內容類型、設定驗證權杖,以及新增自訂標題。 您可以使用 DefaultRequestHeaders 屬性設定要求標頭,或使用 HttpRequestMessage 的 Headers 屬性設定要求標頭。
範例:設定自訂請求標題
在 HttpClient 設定預設的自訂請求標頭
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Custom-Header", "value");
設定自訂請求標頭在 HttpRequestMessage
using var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Custom-Header", "value");
範例:設定一般請求標題
在 .NET 中使用 HttpRequestMessage 時,設定一般請求標題對於向伺服器提供關於所提出要求的其他資訊至關重要。 這些標題可以包含驗證權杖等等。 正確設定這些標體可確保您的 HTTP 要求是由伺服器正確處理。
有關 HttpRequestHeaders 中可用的常見屬性完整清單,請參閱屬性。
若要在 HttpRequestMessage 中設定常見的請求標題,您可以使用 Headers 物件的 HttpRequestMessage 屬性。 此屬性提供 HttpRequestHeaders 集合的存取權,您可以在其中視需要新增或修改標題。
設定常見的預設要求標頭 HttpClient
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "token");
設定常見的請求標頭在 HttpRequestMessage
using System.Net.Http.Headers;
using var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token");
範例:設定內容標題
內容標題可用來提供 HTTP 要求或回應主體的其他資訊。 在 .NET 中使用 HttpClient 時,您可以設定內容標題來指定與傳送或接收內容相關的媒體類型、編碼和其他中繼資料。 正確設定內容標題可確保伺服器和用戶端可以正確地解譯和處理內容。
var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, uri);
// Create the content and set the content headers
var jsonData = "{\"key\":\"value\"}";
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
// The following headers are set automatically by `StringContent`. If you wish to override their values, you can do it like so:
// content.Headers.ContentType = new MediaTypeHeaderValue("application/json; charset=utf-8");
// content.Headers.ContentLength = Encoding.UTF8.GetByteCount(jsonData);
// Assign the content to the request
request.Content = content;
using var response = await client.SendAsync(request);
範例:設定 MaximumErrorResponseLength 在 HttpClient
MaximumErrorResponseLength 使用方式可讓開發人員指定處理程式將緩衝處理之錯誤回應內容的最大長度。 這適用於控制從伺服器收到錯誤回應時,讀取和儲存在記憶體中的資料量。 藉由使用這項技術,您可以在處理大型錯誤回應時防止過多的記憶體使用量,並改善應用程式的效能。
有幾種方法可以這麼做,我們將在此範例中檢查 TruncatedReadStream 技術:
internal sealed class TruncatedReadStream(Stream innerStream, long maxSize) : Stream
{
private long _maxRemainingLength = maxSize;
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override void Flush() => throw new NotSupportedException();
public override int Read(byte[] buffer, int offset, int count)
{
return Read(new Span<byte>(buffer, offset, count));
}
public override int Read(Span<byte> buffer)
{
int readBytes = innerStream.Read(buffer.Slice(0, (int)Math.Min(buffer.Length, _maxRemainingLength)));
_maxRemainingLength -= readBytes;
return readBytes;
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
}
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
int readBytes = await innerStream.ReadAsync(buffer.Slice(0, (int)Math.Min(buffer.Length, _maxRemainingLength)), cancellationToken)
.ConfigureAwait(false);
_maxRemainingLength -= readBytes;
return readBytes;
}
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
public override ValueTask DisposeAsync() => innerStream.DisposeAsync();
protected override void Dispose(bool disposing)
{
if (disposing)
{
innerStream.Dispose();
}
}
}
和 TruncatedReadStream 的使用範例:
int maxErrorResponseLength = 1 * 1024; // 1 KB
HttpClient client = new HttpClient();
using HttpResponseMessage response = await client.GetAsync(Uri);
if (response.Content is not null)
{
Stream responseReadStream = await response.Content.ReadAsStreamAsync();
// If MaxErrorResponseLength is set and the response status code is an error code, then wrap the response stream in a TruncatedReadStream
if (maxErrorResponseLength >= 0 && !response.IsSuccessStatusCode)
{
responseReadStream = new TruncatedReadStream(responseReadStream, maxErrorResponseLength);
}
// Read the response stream
Memory<byte> buffer = new byte[1024];
int readValue = await responseReadStream.ReadAsync(buffer);
}
範例:套用 CachePolicy 標頭
警告
HttpClient 沒有快取回應的內建邏輯。 除了自行實施所有快取之外,沒有因應措施。 僅僅設置標頭無法實現緩存。
從 HttpWebRequest 移轉到 HttpClient 時,正確處理 pragma 和 cache-control 等快取相關的標題非常重要。 這些標題會控制如何快取和擷取回應,確保應用程式在效能和資料新鮮度方面如預期般運作。
在 HttpWebRequest 中,您可能已經使用 CachePolicy 屬性來設定這些標題。 不過,在 HttpClient 中,您必須在要求上手動設定這些標題。
使用HttpWebRequest的舊程式代碼:
HttpWebRequest request = WebRequest.CreateHttp(uri);
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
在較舊的 HttpWebRequest API 中,由於此功能的內建支援,所以套用 CachePolicy 相當簡單。 不過,較新 HttpClient API 不提供相同的內建功能。 儘管如此,您仍可透過實施 AddCacheControlHeaders 來達成相似的行為,手動新增快取相關的標題。
使用 HttpClient的新程式代碼:
public static class CachePolicy
{
public static void AddCacheControlHeaders(HttpRequestMessage request, RequestCachePolicy policy)
如需 AddCacheControlHeaders 的實作,請參閱 AddCacheControlHeaders.cs。
AddCacheControlHeaders使用情況:
static async Task AddCacheControlHeaders()
{
HttpClient client = new HttpClient();
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, Uri);
CachePolicy.AddCacheControlHeaders(requestMessage, new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore));
HttpResponseMessage response = await client.SendAsync(requestMessage);
}
緩衝屬性的使用方式
從 HttpWebRequest 遷移到 HttpClient時,請務必瞭解這兩個 API 處理緩衝的方式差異。
使用HttpWebRequest的舊程式代碼:
在 HttpWebRequest 中,您可以透過 AllowWriteStreamBuffering 和 AllowReadStreamBuffering 屬性直接控制緩衝屬性。 這些屬性會啟用或停用從伺服器傳送和接收之資料的緩衝處理。
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AllowReadStreamBuffering = true; // Default is `false`.
request.AllowWriteStreamBuffering = false; // Default is `true`.
使用 HttpClient的新程式代碼:
在 HttpClient 中,沒有直接等同於 AllowWriteStreamBuffering 和 AllowReadStreamBuffering 的屬性。
HttpClient不會自行緩衝請求主體,而是將這一責任委派給使用的HttpContent。 內容像StringContent或ByteArrayContent在邏輯上已經緩衝於記憶體中,而使用StreamContent預設下不會產生任何緩衝。 若要強制緩衝處理內容,您可以在傳送要求之前呼叫 HttpContent.LoadIntoBufferAsync。 以下是範例:
HttpClient client = new HttpClient();
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = new StreamContent(yourStream);
await request.Content.LoadIntoBufferAsync();
using HttpResponseMessage response = await client.SendAsync(request);
在 HttpClient 中,讀取緩衝預設為啟用。 若要避免,您可以指定 HttpCompletionOption.ResponseHeadersRead 標誌,或使用 GetStreamAsync 協助程式。
HttpClient client = new HttpClient();
using HttpResponseMessage response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
await using Stream responseStream = await response.Content.ReadAsStreamAsync();
// Or simply
await using Stream responseStream = await client.GetStreamAsync(uri);