HttpWebRequest 至 HttpClient 移轉指南
本文旨在引導開發人員完成從 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 | 這對 HttpClient 來說永遠是 true 。 |
Timeout |
Timeout | |
TransferEncoding |
TransferEncoding | 範例:設定請求標題。 |
UnsafeAuthenticatedConnectionSharing |
沒有同等項目 API | 沒有因應措施 |
UseDefaultCredentials |
沒有直接的對等 API | 範例:設定 SocketsHttpHandler 屬性。 |
UserAgent |
UserAgent | 範例:設定請求標題。 |
移轉 ServicePoint(Manager) 使用方式
您應該注意,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();
var request = new HttpRequestMessage(HttpMethod.Post, "https://example.com"); // Method and RequestUri usage
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 的使用方式
SocketsHttpHandler
中的 ConnectCallback
屬性允許開發人員自訂建立 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 保持運作,您可以在 HttpClient
使用套接字之前使用 ConnectCallback
來設定通訊端。 事實上,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 時,您可以使用 SocketsHttpHandler 的 ConnectCallback 屬性,手動處理 DNS 解析並輪替 IP 位址,以實作 DNS 循環配置資源。
若要使用 HttpClient 啟用 DNS 循環配置資源,您可以使用 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
的實施情況。
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 檢查憑證撤銷清單
SocketsHttpHandler.SslOptions
中的 CheckCertificateRevocationList
屬性可讓開發人員在 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);
範例:擷取憑證
若要從 HttpClient
的 RemoteCertificateValidationCallback
取得憑證,您可以使用 HttpClientHandler
或 SocketsHttpHandler.SslOptions
的 ServerCertificateCustomValidationCallback
屬性。 此回呼可讓您在 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 服務通訊。
設定請求標題
請求標題可用來向伺服器提供有關所提出要求的其他資訊。 常見的使用案例包括指定內容類型、設定驗證權杖,以及新增自訂標題。 您可以使用 HttpClient
的 DefaultRequestHeaders
屬性或 HttpRequestMessage
的標題屬性來設定請求標題。
範例:設定自訂請求標題
在 HttpClient 中設定預設自訂請求標題
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Custom-Header", "value");
在 HttpRequestMessage 中設定自訂請求標題
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Custom-Header", "value");
範例:設定一般請求標題
在 .NET 中使用 HttpRequestMessage
時,設定一般請求標題對於向伺服器提供關於所提出要求的其他資訊至關重要。 這些標題可以包含驗證權杖等等。 正確設定這些標體可確保您的 HTTP 要求是由伺服器正確處理。
有關 HttpRequestHeaders 中可用的常見屬性完整清單,請參閱屬性。
若要在 HttpRequestMessage
中設定常見的請求標題,您可以使用 HttpRequestMessage
物件的 Headers
屬性。 此屬性提供 HttpRequestHeaders
集合的存取權,您可以在其中視需要新增或修改標題。
在 HttpClient 中設定常見預設請求標題
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "token");
在 HttpRequestMessage 中設定常見請求標題
using System.Net.Http.Headers;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token");
範例:設定內容標題
內容標題可用來提供 HTTP 要求或回應主體的其他資訊。 在 .NET 中使用 HttpClient
時,您可以設定內容標題來指定與傳送或接收內容相關的媒體類型、編碼和其他中繼資料。 正確設定內容標題可確保伺服器和用戶端可以正確地解譯和處理內容。
var client = new HttpClient();
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);
範例:在 HttpClient 中設定 MaximumErrorResponseLength
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
使用情況:
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();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = new StreamContent(yourStream);
await request.Content.LoadIntoBufferAsync();
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);