Freigeben über


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

HttpWebRequestAlte API Neue API Hinweise
Accept Accept Beispiel: Festlegen von Anforderungsheadern.
Address RequestUri Beispiel: Abrufen von umgeleiteten URI.
AllowAutoRedirect AllowAutoRedirect Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
AllowReadStreamBuffering Keine direkte entsprechende API Verwendung von Puffereigenschaften.
AllowWriteStreamBuffering Keine direkte entsprechende API Verwendung von Puffereigenschaften.
AuthenticationLevel Keine direkte entsprechende API Beispiel: Aktivieren der gegenseitigen Authentifizierung.
AutomaticDecompression AutomaticDecompression Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
CachePolicy Keine direkte entsprechende API Beispiel: Anwenden von CachePolicy-Headern.
ClientCertificates SslOptions.ClientCertificates Verwendung von zertifikatbezogenen Eigenschaften in HttpClient.
Connection Connection Beispiel: Festlegen von Anforderungsheadern.
ConnectionGroupName Keine entsprechende API Keine Problemumgehung
ContentLength ContentLength Beispiel: Festlegen von Inhaltsheadern.
ContentType ContentType Beispiel: Festlegen von Inhaltsheadern.
ContinueDelegate Keine entsprechende API Keine Problemumgehung.
ContinueTimeout Expect100ContinueTimeout Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
CookieContainer CookieContainer Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
Credentials Credentials Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
Date Date Beispiel: Festlegen von Anforderungsheadern.
DefaultCachePolicy Keine direkte entsprechende API Beispiel: Anwenden von CachePolicy-Headern.
DefaultMaximumErrorResponseLength Keine direkte entsprechende API Beispiel: Festlegen von MaximumErrorResponseLength in HttpClient.
DefaultMaximumResponseHeadersLength Keine entsprechende API MaxResponseHeadersLength kann stattdessen verwendet werden.
DefaultWebProxy Keine entsprechende API Proxy kann stattdessen verwendet werden.
Expect Expect Beispiel: Festlegen von Anforderungsheadern.
HaveResponse Keine entsprechende API Impliziert, indem eine HttpResponseMessage Instanz verwendet wird.
Headers Headers Beispiel: Festlegen von Anforderungsheadern.
Host Host Beispiel: Festlegen von Anforderungsheadern.
IfModifiedSince IfModifiedSince Beispiel: Festlegen von Anforderungsheadern.
ImpersonationLevel Keine direkte entsprechende API Beispiel: Ändern des Identitätswechsellevels.
KeepAlive Keine direkte entsprechende API Beispiel: Festlegen von Anforderungsheadern.
MaximumAutomaticRedirections MaxAutomaticRedirections Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
MaximumResponseHeadersLength MaxResponseHeadersLength Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
MediaType Keine direkte entsprechende API Beispiel: Festlegen von Inhaltsheadern.
Method Method Beispiel: Verwendung von HttpRequestMessage-Eigenschaften.
Pipelined Keine entsprechende API HttpClient unterstützt keine Pipelining.
PreAuthenticate PreAuthenticate
ProtocolVersion HttpRequestMessage.Version Beispiel: Verwendung von HttpRequestMessage-Eigenschaften.
Proxy Proxy Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
ReadWriteTimeout Keine direkte entsprechende API Verwendung von SocketsHttpHandler und ConnectCallback.
Referer Referrer Beispiel: Festlegen von Anforderungsheadern.
RequestUri RequestUri Beispiel: Verwendung von HttpRequestMessage-Eigenschaften.
SendChunked TransferEncodingChunked Beispiel: Festlegen von Anforderungsheadern.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
ServicePoint Keine entsprechende API ServicePoint ist nicht Teil von HttpClient.
SupportsCookieContainer Keine entsprechende API Dies ist immer true für HttpClient.
Timeout Timeout
TransferEncoding TransferEncoding Beispiel: Festlegen von Anforderungsheadern.
UnsafeAuthenticatedConnectionSharing Keine entsprechende API Keine Problemumgehung
UseDefaultCredentials Keine direkte entsprechende API Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
UserAgent UserAgent Beispiel: Festlegen von Anforderungsheadern.

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

ServicePointManagerAlte API Neue API Hinweise
CheckCertificateRevocationList SslOptions.CertificateRevocationCheckMode Beispiel: Aktivieren der CRL-Überprüfung mit SocketsHttpHandler.
DefaultConnectionLimit MaxConnectionsPerServer Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
DnsRefreshTimeout Keine entsprechende API Beispiel: Aktivieren von DNS-Roundrobin.
EnableDnsRoundRobin Keine entsprechende API Beispiel: Aktivieren von DNS-Roundrobin.
EncryptionPolicy SslOptions.EncryptionPolicy Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
Expect100Continue ExpectContinue Beispiel: Festlegen von Anforderungsheadern.
MaxServicePointIdleTime PooledConnectionIdleTimeout Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
MaxServicePoints Keine entsprechende API ServicePoint ist nicht Teil von HttpClient.
ReusePort Keine direkte entsprechende API Verwendung von SocketsHttpHandler und ConnectCallback.
SecurityProtocol SslOptions.EnabledSslProtocols Beispiel: Festlegen von SocketsHttpHandler-Eigenschaften.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Beide sind RemoteCertificateValidationCallback
UseNagleAlgorithm Keine direkte entsprechende API Verwendung von SocketsHttpHandler und ConnectCallback.

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.

DnsRoundRobinConnectorVerwendung:

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.

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.

AddCacheControlHeadersVerwendung:

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);