Leitfaden für die Migration von HttpWebRequest zu HttpClient
Dieser Artikel führt Entwickler durch den Prozess der Migration von HttpWebRequest, ServicePoint und ServicePointManager zu HttpClient. Die Migration ist aufgrund der Obsoleszenz der älteren APIs und der zahlreichen Vorteile erforderlich, die durch HttpClientverbesserte Leistung, bessere Ressourcenverwaltung und ein moderneres und flexibleres API-Design geboten werden. Wenn Entwickler die in diesem Dokument beschriebenen Schritte befolgen, können sie ihre Codebasen reibungslos umstellen und die von HttpClient bereitgestellten Features vollständig nutzen.
Warnung
Die Migration von HttpWebRequest
, ServicePoint
und ServicePointManager
zu HttpClient
ist nicht nur eine "schön zu haben" Leistungsverbesserung. Ihnen muss klar sein, dass die Leistung der vorhandenen WebRequest
Logik wahrscheinlich erheblich sinkt, sobald Sie zu .NET (Core) wechseln. Das liegt daran, dass WebRequest
als minimale Kompatibilitätsschicht beibehalten wird, was bedeutet, dass viele Optimierungen fehlen, z. B. die Wiederverwendung von Verbindungen in zahlreichen Fällen. Daher ist der Übergang zu HttpClient
ist eine wichtige Voraussetzung, um sicherzustellen, dass die Leistung und das Ressourcenmanagement Ihrer Anwendung den modernen Standards entsprechen.
Migrieren von HttpWebRequest zu HttpClient
Beginnen wir mit einigen Beispielen:
Einfache GET-Anforderung mithilfe von HttpWebRequest
Hier ist ein Beispiel dafür, wie der Code aussehen könnte:
HttpWebRequest request = WebRequest.CreateHttp(uri);
using WebResponse response = await request.GetResponseAsync();
Einfache GET-Anforderung mithilfe von HttpClient
Hier ist ein Beispiel dafür, wie der Code aussehen könnte:
HttpClient client = new();
using HttpResponseMessage message = await client.GetAsync(uri);
Einfache POST-Anforderung mithilfe von HttpWebRequest
Hier ist ein Beispiel dafür, wie der Code aussehen könnte:
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();
Einfache POST-Anforderung mithilfe von HttpClient
Hier ist ein Beispiel dafür, wie der Code aussehen könnte:
HttpClient client = new();
using HttpResponseMessage responseMessage = await client.PostAsync(uri, new StringContent("Hello World!"));
HttpWebRequest zu HttpClient, SocketsHttpHandler-Migrationshandbuch
Migrieren der ServicePoint(Manager)-Verwendung
Sie sollten sich bewusst sein, dass ServicePointManager
eine statische Klasse ist. Das bedeutet, dass alle Änderungen, die an ihren Eigenschaften vorgenommen wurden, global Auswirkungen auf alle neu erstellten ServicePoint
-Objekte innerhalb der Anwendung haben. Beispielsweise wirken sich Änderungen an einer Eigenschaft wie ConnectionLimit
oder Expect100Continue
auf jede neue ServicePoint-Instanz aus.
Warnung
In modernem .NET werden keine Konfigurationen berücksichtigt, HttpClient
die auf ServicePointManager
.
Zuordnung von ServicePointManager-Eigenschaften
Warnung
In modernem .NET sind die Standardwerte für die UseNagleAlgorithm
- und Expect100Continue
-Eigenschaften auf festgelegt false
. Diese Werte waren true
standardmäßig in .NET Framework.
Zuordnung von ServicePointManager-Methoden
ServicePointManagerAlte API | Neue API | Hinweise |
---|---|---|
FindServicePoint |
Keine entsprechende API | Keine Problemumgehung |
SetTcpKeepAlive |
Keine direkte entsprechende API | Verwendung von SocketsHttpHandler und ConnectCallback. |
Zuordnung von ServicePoint-Eigenschaften
ServicePointAlte API | Neue API | Hinweise |
---|---|---|
Address |
HttpRequestMessage.RequestUri |
Dies ist anforderungs-URI, diese Informationen finden Sie unter HttpRequestMessage . |
BindIPEndPointDelegate |
Keine direkte entsprechende API | Verwendung von SocketsHttpHandler und ConnectCallback. |
Certificate |
Keine direkte entsprechende API | Diese Informationen können abgerufen werden von RemoteCertificateValidationCallback . Beispiel: Abrufen des Zertifikats. |
ClientCertificate |
Keine entsprechende API | Beispiel: Aktivieren der gegenseitigen Authentifizierung. |
ConnectionLeaseTimeout |
SocketsHttpHandler.PooledConnectionLifetime |
Entsprechende Einstellung in HttpClient |
ConnectionLimit |
MaxConnectionsPerServer | Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften. |
ConnectionName |
Keine entsprechende API | Keine Problemumgehung |
CurrentConnections |
Keine entsprechende API | Siehe Netzwerktelemetrie in .NET |
Expect100Continue |
ExpectContinue | Beispiel: Festlegen von Anforderungsheadern. |
IdleSince |
Keine entsprechende API | Keine Problemumgehung |
MaxIdleTime |
PooledConnectionIdleTimeout | Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften. |
ProtocolVersion |
HttpRequestMessage.Version |
Beispiel: Verwendung von HttpRequestMessage-Eigenschaften. |
ReceiveBufferSize |
Keine direkte entsprechende API | Verwendung von SocketsHttpHandler und ConnectCallback. |
SupportsPipelining |
Keine entsprechende API | HttpClient unterstützt keine Pipelining. |
UseNagleAlgorithm |
Keine direkte entsprechende API | Verwendung von SocketsHttpHandler und ConnectCallback. |
Zuordnung von ServicePoint-Methoden
ServicePointAlte API | Neue API | Hinweise |
---|---|---|
CloseConnectionGroup |
Keine Entsprechung | Keine Problemumgehung |
SetTcpKeepAlive |
Keine direkte entsprechende API | Verwendung von SocketsHttpHandler und ConnectCallback. |
Verwendung der HttpClient- und HttpRequestMessage-Eigenschaften
Beim Arbeiten mit HttpClient in .NET haben Sie Zugriff auf eine Vielzahl von Eigenschaften, mit denen Sie HTTP-Anforderungen und -Antworten konfigurieren und anpassen können. Wenn Sie diese Eigenschaften verstehen, können Sie HttpClient optimal nutzen und sicherstellen, dass Ihre Anwendung effizient und sicher mit Webdiensten kommuniziert.
Siehe: Beispiel: Verwendung von HttpRequestMessage-Eigenschaften.
Hier ist ein Beispiel für die gemeinsame Verwendung von HttpClient und 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`.
Beispiel: Abrufen von umgeleiteten URI
Hier ist ein Beispiel für das Abrufen von umgeleiteten URI (Identisch mit HttpWebRequest.Address
):
var client = new HttpClient();
using var response = await client.GetAsync(uri);
var redirectedUri = response.RequestMessage.RequestUri;
Siehe: Verwendung von SocketsHttpHandler und ConnectCallback.
Mit der ConnectCallback
Eigenschaft in SocketsHttpHandler
können Entwickler den Prozess der Einrichtung einer TCP-Verbindung anpassen. Dies kann für Szenarien hilfreich sein, in denen Sie die DNS-Auflösung steuern oder bestimmte Socketoptionen auf die Verbindung anwenden müssen. Mithilfe von ConnectCallback
können Sie den Verbindungsprozess abfangen und ändern, bevor er von HttpClient
verwendet wird.
Beispiel: Binden der IP-Adresse an Socket
Im alten Ansatz mit der Verwendung von HttpWebRequest
haben Sie möglicherweise benutzerdefinierte Logik verwendet, um eine bestimmte IP-Adresse an einen Socket zu binden. Hier erfahren Sie, wie Sie ähnliche Funktionen mit HttpClient
und ConnectCallback
verwenden:
Alter Code mit 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();
Neuer Code verwendet HttpClient
und 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);
Beispiel: Anwenden bestimmter Socketoptionen
Wenn Sie bestimmte Socketoptionen anwenden müssen, z. B. das Aktivieren von TCP-Keep-Alive, können Sie ConnectCallback
verwenden, um den Socket konfigurieren, bevor er von HttpClient
verwendet wird. Tatsächlich ist ConnectCallback
flexibler, um Socketoptionen zu konfigurieren.
Alter Code mit 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();
Neuer Code verwendet HttpClient
und 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);
Beispiel: Aktivieren von DNS-Roundrobin
DNS Round Robin ist eine Technik, die verwendet wird, um Den Netzwerkdatenverkehr über mehrere Server zu verteilen, indem sie durch eine Liste der IP-Adressen gedreht wird, die einem einzelnen Domänennamen zugeordnet sind. Dies hilft beim Lastenausgleich und bei der Verbesserung der Verfügbarkeit von Diensten. Bei Verwendung von HttpClient können Sie DNS Round Robin implementieren, indem Sie die DNS-Auflösung manuell behandeln und mithilfe der ConnectCallback-Eigenschaft von SocketsHttpHandler durch die IP-Adressen drehen.
Um DNS Round Robin mit HttpClient zu aktivieren, können Sie die ConnectCallback-Eigenschaft verwenden, um die DNS-Einträge manuell aufzulösen und durch die IP-Adressen zu drehen. Hier ist ein Beispiel für HttpWebRequest
und HttpClient
:
Alter Code mit HttpWebRequest
:
ServicePointManager.DnsRefreshTimeout = 60000;
ServicePointManager.EnableDnsRoundRobin = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
In der älteren HttpWebRequest
API war die Aktivierung von DNS Round Robin aufgrund der integrierten Unterstützung für dieses Feature einfach. Die neuere HttpClient
API stellt jedoch nicht die gleiche integrierte Funktionalität bereit. Trotzdem können Sie ein ähnliches Verhalten erzielen, indem Sie ein DnsRoundRobinConnector
, das manuell durch die IP-Adressen rotiert wird, die von der DNS-Auflösung zurückgegeben werden.
Neuer Code mit 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
Die Implementierung von DnsRoundRobinConnector
finden Sie hier.
DnsRoundRobinConnector
Verwendung:
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);
}
Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften
SocketsHttpHandler ist ein leistungsstarker und flexibler Handler in .NET, der erweiterte Konfigurationsoptionen für die Verwaltung von HTTP-Verbindungen bereitstellt. Indem Sie verschiedene Eigenschaften von SocketsHttpHandler festlegen, können Sie das Verhalten Ihres HTTP-Clients optimieren, um bestimmte Anforderungen zu erfüllen, z. B. Leistungsoptimierung, Sicherheitsverbesserungen und benutzerdefinierte Verbindungsbehandlung.
Hier ist ein Beispiel für die Konfiguration von SocketsHttpHandler mit verschiedenen Eigenschaften und deren Verwendung mit 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);
Beispiel: Ändern des Identitätswechsellevels.
Diese Funktionalität ist spezifisch für bestimmte Plattformen und ist etwas veraltet. Wenn Sie eine Problemumgehung benötigen, können Sie sich auf diesen Abschnitt des Codes beziehen.
Verwendung von Zertifikaten und TLS-bezogenen Eigenschaften in HttpClient
Beim Arbeiten mit HttpClient
müssen Sie möglicherweise Clientzertifikate für verschiedene Zwecke verarbeiten, z. B. eine benutzerdefinierte Überprüfung von Serverzertifikaten oder das Abrufen des Serverzertifikats. HttpClient
bietet mehrere Eigenschaften und Optionen zum effektiven Verwalten von Zertifikaten.
Beispiel: Überprüfen der Zertifikatsperrliste mit SocketsHttpHandler
Die CheckCertificateRevocationList
-Eigenschaft in SocketsHttpHandler.SslOptions
ermöglicht Entwicklern, die Überprüfung auf Zertifikatsperrlisten (CRL) während SSL/TLS handshake zu aktivieren oder zu deaktivieren. Durch Aktivieren dieser Eigenschaft wird sichergestellt, dass der Client überprüft, ob das Zertifikat des Servers widerrufen wurde, wodurch die Sicherheit der Verbindung verbessert wird.
Alter Code mit HttpWebRequest
:
ServicePointManager.CheckCertificateRevocationList = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Neuer Code mit 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);
Beispiel: Abrufen des Zertifikats
Um das Zertifikat aus dem RemoteCertificateValidationCallback
in HttpClient
abzurufen, können Sie die ServerCertificateCustomValidationCallback
-Eigenschaft von HttpClientHandler
oder SocketsHttpHandler.SslOptions
verwenden. Mit diesem Callback können Sie das Zertifikat des Servers während des SSL/TLS-Handshakes überprüfen.
Alter Code mit HttpWebRequest
:
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
X509Certificate? serverCertificate = request.ServicePoint.Certificate;
Neuer Code mit 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");
Beispiel: Aktivieren der gegenseitigen Authentifizierung
Die gegenseitige Authentifizierung, auch als bidirektionale SSL- oder Clientzertifikatauthentifizierung bezeichnet, ist ein Sicherheitsprozess, bei dem sich sowohl der Client als auch der Server gegenseitig authentifizieren. Dadurch wird sichergestellt, dass beide Parteien deren Anspruch haben, eine zusätzliche Sicherheitsebene für vertrauliche Kommunikationen bereitzustellen. In HttpClient
können Sie die gegenseitige Authentifizierung aktivieren, indem Sie HttpClientHandler
oder SocketsHttpHandler
so konfigurieren, dass das Client-Zertifikat eingeschlossen und das Zertifikat des Servers validiert wird.
Führen Sie die folgenden Schritte aus, um die gegenseitige Authentifizierung zu aktivieren:
- Laden des Clientzertifikats
- Konfigurieren Sie den HttpClientHandler oder SocketsHttpHandler so, dass er das Clientzertifikat enthält.
- Richten Sie den Serverzertifikat-Überprüfungscallback ein, wenn eine benutzerdefinierte Überprüfung erforderlich ist.
Hier sehen Sie ein Beispiel zur Verwendung von 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);
Verwendung von Headereigenschaften
Header spielen eine wichtige Rolle bei der HTTP-Kommunikation und stellen wesentliche Metadaten über die Anforderung und Antwort bereit. Beim Arbeiten mit HttpClient
in .NET können Sie verschiedene Headereigenschaften festlegen und verwalten, um das Verhalten Ihrer HTTP-Anforderungen und -Antworten zu steuern. Wenn Sie wissen, wie Sie diese Headereigenschaften effektiv verwenden, können Sie sicherstellen, dass Ihre Anwendung effizient und sicher mit Webdiensten kommuniziert.
Festlegen von Anforderungsheadern
Anforderungsheader werden verwendet, um dem Server zusätzliche Informationen über die zu stellende Anforderung bereitzustellen. Häufige Anwendungsfälle sind das Angeben des Inhaltstyps, das Festlegen von Authentifizierungstoken und das Hinzufügen von benutzerdefinierten Headern. Sie können Anforderungsheader mithilfe der DefaultRequestHeaders
-Eigenschaft von HttpClient
oder der Headers-Eigenschaft von HttpRequestMessage
festlegen.
Beispiel: Festlegen von benutzerdefinierten Anforderungsheadern
FFestlegen von standardmäßigen benutzerdefinierten Anforderungsheadern in HttpClient
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Custom-Header", "value");
Festlegen von benutzerdefinierten Anforderungsheadern in HttpRequestMessage
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Custom-Header", "value");
Beispiel: Festlegen allgemeiner Anforderungsheader
Beim Arbeiten mit HttpRequestMessage
in .NET ist das Festlegen allgemeiner Anforderungsheader für die Bereitstellung zusätzlicher Informationen an den Server über die zu stellende Anforderung unerlässlich. Diese Header können Authentifizierungstoken und mehr enthalten. Durch die ordnungsgemäße Konfiguration dieser Header wird sichergestellt, dass Ihre HTTP-Anforderungen vom Server ordnungsgemäß verarbeitet werden.
Eine umfassende Liste allgemeiner Eigenschaften, die in HttpRequestHeaders verfügbar sind, finden Sie unter Eigenschaften.
Um allgemeine Anforderungsheader in HttpRequestMessage
festzulegen können Sie die Headers
-Eigenschaft des HttpRequestMessage
-Objekts verwenden. Diese Eigenschaft ermöglicht den Zugriff auf die HttpRequestHeaders
-Sammlung, wo Sie bei Bedarf Header hinzufügen oder ändern können.
Festlegen allgemeiner Standardanforderungsheader in HttpClient
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "token");
Festlegen von allgemeinen Anforderungsheadern in HttpRequestMessage
using System.Net.Http.Headers;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token");
Beispiel: Festlegen von Inhaltsheadern
Inhaltsheader werden verwendet, um zusätzliche Informationen zum Textkörper einer HTTP-Anforderung oder -Antwort bereitzustellen. Wenn Sie mit HttpClient
in .NET arbeiten, können Sie Inhaltsheader festlegen, um den Medientyp, die Kodierung und andere Metadaten anzugeben, die sich auf den gesendeten oder empfangenen Inhalt beziehen. Durch die ordnungsgemäße Konfiguration von Inhaltsheadern wird sichergestellt, dass der Server und der Client den Inhalt richtig interpretieren und verarbeiten können.
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);
Beispiel: MaximumErrorResponseLength in HttpClient festlegen.
Mithilfe der MaximumErrorResponseLength
-Verwendung können Entwickler die maximale Länge des Fehlerantwortinhalts angeben, den der Handler puffert. Dies ist nützlich, um die Datenmenge zu steuern, die gelesen und im Arbeitsspeicher gespeichert wird, wenn eine Fehlerantwort vom Server empfangen wird. Mithilfe dieser Technik können Sie eine übermäßige Speicherauslastung verhindern und die Leistung Ihrer Anwendung bei der Behandlung großer Fehlerantworten verbessern.
Es gibt verschiedene Möglichkeiten, dies zu tun, wir untersuchen die TruncatedReadStream
-Technik in diesem Beispiel:
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();
}
}
}
Und Verwendungsbeispiel für 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);
}
Beispiel: Anwenden von CachePolicy-Headern
Warnung
HttpClient
verfügt nicht über integrierte Logik zum Zwischenspeichern von Antworten. Es gibt keine Andere Problemumgehung als die Implementierung aller Zwischenspeicherung selbst. Durch einfaches Festlegen der Header wird keine Zwischenspeicherung erzielt.
Bei der Migration von HttpWebRequest
zu HttpClient
ist die korrekte Handhabung cachebezogene Header wie pragma
und cache-control
wichtig. Diese Header steuern, wie Antworten zwischengespeichert und abgerufen werden, um sicherzustellen, dass sich Ihre Anwendung hinsichtlich Leistung und Datenfrische erwartungsgemäß verhält.
In HttpWebRequest
haben Sie möglicherweise die CachePolicy
-Eigenschaft verwendet, um diese Header festzulegen. In HttpClient
müssen Sie diese Header jedoch manuell für die Anforderung festlegen.
Alter Code mit HttpWebRequest
:
HttpWebRequest request = WebRequest.CreateHttp(uri);
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
In der älteren HttpWebRequest
API war die Anwendung von CachePolicy
aufgrund der integrierten Unterstützung für dieses Feature einfach. Die neuere HttpClient
API stellt jedoch nicht die gleiche integrierte Funktionalität bereit. Trotzdem können Sie ein ähnliches Verhalten erreichen, indem Sie ein AddCacheControlHeaders
implementieren, das manuell cachebezogene Header hinzufügt.
Neuer Code mit HttpClient
:
public static class CachePolicy
{
public static void AddCacheControlHeaders(HttpRequestMessage request, RequestCachePolicy policy)
Die Implementierung von AddCacheControlHeaders
finden Sie hier.
AddCacheControlHeaders
Verwendung:
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);
}
Verwendung von Puffereigenschaften
Bei der Migration von HttpWebRequest zu HttpClient
ist es wichtig, die Unterschiede bei der Behandlung der Pufferung dieser beiden APIs zu verstehen.
Alter Code mit HttpWebRequest
:
In HttpWebRequest
haben Sie direkte Kontrolle über puffernde Eigenschaften über die AllowWriteStreamBuffering
und AllowReadStreamBuffering
Eigenschaften. Diese Eigenschaften aktivieren oder deaktivieren die Pufferung von Daten, die an den Server gesendet und empfangen werden.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AllowReadStreamBuffering = true; // Default is `false`.
request.AllowWriteStreamBuffering = false; // Default is `true`.
Neuer Code mit HttpClient
:
In HttpClient
gibt es keine direkten Entsprechungen zu den AllowWriteStreamBuffering
und AllowReadStreamBuffering
Eigenschaften.
HttpClient puffert keine eigenen Anforderungstexte, sondern delegiert die Verantwortung an die HttpContent
verwendete. Inhalte wie StringContent
oder ByteArrayContent
sind logisch bereits im Arbeitsspeicher gepuffert, während die Verwendung von StreamContent
standardmäßig keine Pufferung verursacht. Um zu erzwingen, dass der Inhalt gepuffert wird, können Sie vor dem Senden der Anforderung HttpContent.LoadIntoBufferAsync
abrufen. Hier sehen Sie ein Beispiel:
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);
In HttpClient
ist die Lesepufferung standardmäßig aktiviert. Um dies zu vermeiden, können Sie die HttpCompletionOption.ResponseHeadersRead
Flag angeben oder das GetStreamAsync
Hilfsprogramm verwenden.
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);