Udostępnij za pośrednictwem


Przewodnik migracji httpWebRequest do klienta HttpClient

Ten artykuł ma na celu poprowadzenie deweloperów przez proces migracji z HttpWebRequestsystemu , ServicePointi ServicePointManager do HttpClient. Migracja jest konieczna ze względu na przestarzałe interfejsy API i liczne korzyści oferowane przez HttpClientusługę , w tym lepszą wydajność, lepsze zarządzanie zasobami oraz bardziej nowoczesny i elastyczny projekt interfejsu API. Wykonując kroki opisane w tym dokumencie, deweloperzy będą mogli bezproblemowo przenosić bazy kodu i korzystać z funkcji udostępnianych przez HttpClientprogram .

Ostrzeżenie

Migrowanie z HttpWebRequestelementu , ServicePointi ServicePointManager do HttpClient nie jest tylko "miłą wydajnością". Ważne jest, aby zrozumieć, że wydajność istniejącej WebRequest logiki może znacznie obniżyć się po przejściu do platformy .NET (Core). Dzieje się tak dlatego, że WebRequest jest utrzymywana jako minimalna warstwa zgodności, co oznacza, że brakuje wielu optymalizacji, takich jak ponowne użycie połączenia w wielu przypadkach. W związku z tym przejście do HttpClient systemu jest niezbędne, aby zapewnić, że wydajność aplikacji i zarządzanie zasobami są zgodne z nowoczesnymi standardami.

Migrowanie z HttpWebRequest do HttpClient

Zacznijmy od kilku przykładów:

Proste żądanie GET przy użyciu polecenia HttpWebRequest

Oto przykładowy wygląd kodu:

HttpWebRequest request = WebRequest.CreateHttp(uri);
using WebResponse response = await request.GetResponseAsync();

Proste żądanie GET przy użyciu polecenia HttpClient

Oto przykładowy wygląd kodu:

HttpClient client = new();
using HttpResponseMessage message = await client.GetAsync(uri);

Proste żądanie POST przy użyciu polecenia HttpWebRequest

Oto przykładowy wygląd kodu:

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

Proste żądanie POST przy użyciu polecenia HttpClient

Oto przykładowy wygląd kodu:

HttpClient client = new();
using HttpResponseMessage responseMessage = await client.PostAsync(uri, new StringContent("Hello World!"));

HttpWebRequest to HttpClient, SocketsHttpHandler migration guide (Przewodnik migracji httpWebRequest do klienta HttpClient, SocketsHttpHandler)

HttpWebRequest Stary interfejs API Nowy interfejs API Uwagi
Accept Accept Przykład: ustawianie nagłówków żądań.
Address RequestUri Przykład: pobieranie identyfikatora URI przekierowanego.
AllowAutoRedirect AllowAutoRedirect Przykład: ustawianie właściwości SocketsHttpHandler.
AllowReadStreamBuffering Brak bezpośredniego odpowiednika interfejsu API Użycie właściwości buforowania.
AllowWriteStreamBuffering Brak bezpośredniego odpowiednika interfejsu API Użycie właściwości buforowania.
AuthenticationLevel Brak bezpośredniego odpowiednika interfejsu API Przykład: włączanie wzajemnego uwierzytelniania.
AutomaticDecompression AutomaticDecompression Przykład: ustawianie właściwości SocketsHttpHandler.
CachePolicy Brak bezpośredniego odpowiednika interfejsu API Przykład: Zastosuj nagłówki CachePolicy.
ClientCertificates SslOptions.ClientCertificates Użycie właściwości powiązanych z certyfikatami w programie HttpClient.
Connection Connection Przykład: ustawianie nagłówków żądań.
ConnectionGroupName Brak równoważnego interfejsu API Brak obejścia
ContentLength ContentLength Przykład: ustawianie nagłówków zawartości.
ContentType ContentType Przykład: ustawianie nagłówków zawartości.
ContinueDelegate Brak równoważnego interfejsu API Brak obejścia.
ContinueTimeout Expect100ContinueTimeout Przykład: ustaw właściwości SocketsHttpHandler.
CookieContainer CookieContainer Przykład: ustaw właściwości SocketsHttpHandler.
Credentials Credentials Przykład: ustaw właściwości SocketsHttpHandler.
Date Date Przykład: ustawianie nagłówków żądań.
DefaultCachePolicy Brak bezpośredniego odpowiednika interfejsu API Przykład: Zastosuj nagłówki CachePolicy.
DefaultMaximumErrorResponseLength Brak bezpośredniego odpowiednika interfejsu API Przykład: ustaw wartość MaximumErrorResponseLength w obiekcie HttpClient.
DefaultMaximumResponseHeadersLength Brak równoważnego interfejsu API MaxResponseHeadersLength Zamiast tego można użyć polecenia .
DefaultWebProxy Brak równoważnego interfejsu API Proxy Zamiast tego można użyć polecenia .
Expect Expect Przykład: ustawianie nagłówków żądań.
HaveResponse Brak równoważnego interfejsu API Implikowane przez wystąpienie HttpResponseMessage .
Headers Headers Przykład: ustawianie nagłówków żądań.
Host Host Przykład: ustawianie nagłówków żądań.
IfModifiedSince IfModifiedSince Przykład: ustawianie nagłówków żądań.
ImpersonationLevel Brak bezpośredniego odpowiednika interfejsu API Przykład: Zmiana personifikacjiLevel.
KeepAlive Brak bezpośredniego odpowiednika interfejsu API Przykład: ustawianie nagłówków żądań.
MaximumAutomaticRedirections MaxAutomaticRedirections Przykład: ustawianie właściwości SocketsHttpHandler.
MaximumResponseHeadersLength MaxResponseHeadersLength Przykład: ustawianie właściwości SocketsHttpHandler.
MediaType Brak bezpośredniego odpowiednika interfejsu API Przykład: ustawianie nagłówków zawartości.
Method Method Przykład: użycie właściwości HttpRequestMessage.
Pipelined Brak równoważnego interfejsu API HttpClient nie obsługuje potokowania.
PreAuthenticate PreAuthenticate
ProtocolVersion HttpRequestMessage.Version Przykład: użycie właściwości HttpRequestMessage.
Proxy Proxy Przykład: ustawianie właściwości SocketsHttpHandler.
ReadWriteTimeout Brak bezpośredniego odpowiednika interfejsu API Użycie gniazdHttpHandler i ConnectCallback.
Referer Referrer Przykład: ustawianie nagłówków żądań.
RequestUri RequestUri Przykład: użycie właściwości HttpRequestMessage.
SendChunked TransferEncodingChunked Przykład: ustawianie nagłówków żądań.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Przykład: ustawianie właściwości SocketsHttpHandler.
ServicePoint Brak równoważnego interfejsu API ServicePoint nie jest częścią elementu HttpClient.
SupportsCookieContainer Brak równoważnego interfejsu API Jest to zawsze true dla elementu HttpClient.
Timeout Timeout
TransferEncoding TransferEncoding Przykład: ustawianie nagłówków żądań.
UnsafeAuthenticatedConnectionSharing Brak równoważnego interfejsu API Brak obejścia
UseDefaultCredentials Brak bezpośredniego odpowiednika interfejsu API Przykład: ustawianie właściwości SocketsHttpHandler.
UserAgent UserAgent Przykład: ustawianie nagłówków żądań.

Migrowanie użycia programu ServicePoint(Manager)

Należy pamiętać, że jest to klasa statyczna, co oznacza, że ServicePointManager wszelkie zmiany wprowadzone w jej właściwościach będą miały globalny wpływ na wszystkie nowo utworzone ServicePoint obiekty w aplikacji. Na przykład podczas modyfikowania właściwości, takiej jak ConnectionLimit lub Expect100Continue, ma ona wpływ na każde nowe wystąpienie programu ServicePoint.

Ostrzeżenie

W nowoczesnym środowisku .NET HttpClient nie uwzględnia żadnych konfiguracji ustawionych na platformie ServicePointManager.

ServicePointManager mapowanie właściwości

ServicePointManager Stary interfejs API Nowy interfejs API Uwagi
CheckCertificateRevocationList SslOptions.CertificateRevocationCheckMode Przykład: włączanie sprawdzania listy CRL przy użyciu protokołu SocketsHttpHandler.
DefaultConnectionLimit MaxConnectionsPerServer Przykład: ustawianie właściwości SocketsHttpHandler.
DnsRefreshTimeout Brak równoważnego interfejsu API Przykład: włączanie działania okrężnego dns.
EnableDnsRoundRobin Brak równoważnego interfejsu API Przykład: włączanie działania okrężnego dns.
EncryptionPolicy SslOptions.EncryptionPolicy Przykład: ustawianie właściwości SocketsHttpHandler.
Expect100Continue ExpectContinue Przykład: ustawianie nagłówków żądań.
MaxServicePointIdleTime PooledConnectionIdleTimeout Przykład: ustawianie właściwości SocketsHttpHandler.
MaxServicePoints Brak równoważnego interfejsu API ServicePoint nie jest częścią elementu HttpClient.
ReusePort Brak bezpośredniego odpowiednika interfejsu API Użycie gniazdHttpHandler i ConnectCallback.
SecurityProtocol SslOptions.EnabledSslProtocols Przykład: ustawianie właściwości SocketsHttpHandler.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Oba są RemoteCertificateValidationCallback
UseNagleAlgorithm Brak bezpośredniego odpowiednika interfejsu API Użycie gniazdHttpHandler i ConnectCallback.

Ostrzeżenie

W nowoczesnej platformie .NET wartości domyślne właściwości UseNagleAlgorithm i Expect100Continue są ustawione na false. Te wartości były true domyślnie dostępne w programie .NET Framework.

ServicePointManager mapowanie metody

ServicePointManager Stary interfejs API Nowy interfejs API Uwagi
FindServicePoint Brak równoważnego interfejsu API Brak obejścia
SetTcpKeepAlive Brak bezpośredniego odpowiednika interfejsu API Użycie gniazdHttpHandler i ConnectCallback.

ServicePoint mapowanie właściwości

ServicePoint Stary interfejs API Nowy interfejs API Uwagi
Address HttpRequestMessage.RequestUri Jest to identyfikator URI żądania. Te informacje można znaleźć w obszarze HttpRequestMessage.
BindIPEndPointDelegate Brak bezpośredniego odpowiednika interfejsu API Użycie gniazdHttpHandler i ConnectCallback.
Certificate Brak bezpośredniego odpowiednika interfejsu API Te informacje można pobrać z witryny RemoteCertificateValidationCallback. Przykład: pobieranie certyfikatu.
ClientCertificate Brak równoważnego interfejsu API Przykład: włączanie wzajemnego uwierzytelniania.
ConnectionLeaseTimeout SocketsHttpHandler.PooledConnectionLifetime Ustawienie równoważne w elemencie HttpClient
ConnectionLimit MaxConnectionsPerServer Przykład: ustawianie właściwości SocketsHttpHandler.
ConnectionName Brak równoważnego interfejsu API Brak obejścia
CurrentConnections Brak równoważnego interfejsu API Zobacz Telemetria sieciowa na platformie .NET.
Expect100Continue ExpectContinue Przykład: ustawianie nagłówków żądań.
IdleSince Brak równoważnego interfejsu API Brak obejścia
MaxIdleTime PooledConnectionIdleTimeout Przykład: ustawianie właściwości SocketsHttpHandler.
ProtocolVersion HttpRequestMessage.Version Przykład: użycie właściwości HttpRequestMessage.
ReceiveBufferSize Brak bezpośredniego odpowiednika interfejsu API Użycie gniazdHttpHandler i ConnectCallback.
SupportsPipelining Brak równoważnego interfejsu API HttpClient nie obsługuje potokowania.
UseNagleAlgorithm Brak bezpośredniego odpowiednika interfejsu API Użycie gniazdHttpHandler i ConnectCallback.

ServicePoint mapowanie metody

ServicePoint Stary interfejs API Nowy interfejs API Uwagi
CloseConnectionGroup Brak odpowiednika Brak obejścia
SetTcpKeepAlive Brak bezpośredniego odpowiednika interfejsu API Użycie gniazdHttpHandler i ConnectCallback.

Użycie właściwości HttpClient i HttpRequestMessage

Podczas pracy z klientem HttpClient na platformie .NET masz dostęp do różnych właściwości, które umożliwiają konfigurowanie i dostosowywanie żądań i odpowiedzi HTTP. Zrozumienie tych właściwości może pomóc w jak największym zapewnieniu, że aplikacja komunikuje się wydajnie i bezpiecznie z usługami internetowymi.

Przykład: użycie właściwości HttpRequestMessage

Oto przykład użycia elementów HttpClient i HttpRequestMessage razem:

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`.

Przykład: pobieranie identyfikatora URI przekierowanego

Oto przykład pobierania przekierowanego identyfikatora URI (taki sam jak HttpWebRequest.Address):

var client = new HttpClient();
using var response = await client.GetAsync(uri);
var redirectedUri = response.RequestMessage.RequestUri;

Użycie gniazdHttpHandler i ConnectCallback

Właściwość ConnectCallback w programie SocketsHttpHandler umożliwia deweloperom dostosowywanie procesu nawiązywania połączenia TCP. Może to być przydatne w scenariuszach, w których należy kontrolować rozpoznawanie nazw DNS lub stosować określone opcje gniazda w połączeniu. Za pomocą programu ConnectCallbackmożna przechwycić i zmodyfikować proces połączenia, zanim będzie używany przez HttpClientprogram .

Przykład: wiązanie adresu IP z gniazdem

W przypadku starego podejścia przy użyciu metody HttpWebRequestmożna użyć logiki niestandardowej do powiązania określonego adresu IP z gniazdem. Oto jak można osiągnąć podobną funkcjonalność przy użyciu funkcji HttpClient i ConnectCallback:

Stary kod przy użyciu polecenia 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();

Nowy kod przy użyciu i 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);

Przykład: Stosowanie określonych opcji gniazd

Jeśli musisz zastosować określone opcje gniazda, takie jak włączanie zachowania aktywności protokołu TCP, możesz użyć ConnectCallback polecenia , aby skonfigurować gniazdo przed użyciem przez HttpClientprogram . W rzeczywistości ConnectCallback jest bardziej elastyczny do konfigurowania opcji gniazd.

Stary kod przy użyciu polecenia 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();

Nowy kod przy użyciu i 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);

Przykład: włączanie działania okrężnego DNS

Działanie okrężne DNS to technika służąca do dystrybucji ruchu sieciowego między wieloma serwerami przez rotację za pośrednictwem listy adresów IP skojarzonych z jedną nazwą domeny. Pomaga to w równoważeniu obciążenia i poprawie dostępności usług. W przypadku korzystania z klienta HttpClient można zaimplementować działanie okrężne DNS, ręcznie obsługując rozpoznawanie nazw DNS i obracając się za pośrednictwem adresów IP przy użyciu właściwości ConnectCallback socketsHttpHandler.

Aby włączyć działanie okrężne DNS za pomocą klienta HttpClient, możesz użyć właściwości ConnectCallback, aby ręcznie rozpoznać wpisy DNS i obracać się za pośrednictwem adresów IP. Oto przykład dla HttpWebRequest i HttpClient:

Stary kod przy użyciu polecenia HttpWebRequest:

ServicePointManager.DnsRefreshTimeout = 60000;
ServicePointManager.EnableDnsRoundRobin = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();

W starszym HttpWebRequest interfejsie API włączenie działania okrężnego DNS było proste ze względu na wbudowaną obsługę tej funkcji. Jednak nowszy HttpClient interfejs API nie zapewnia tej samej wbudowanej funkcjonalności. Mimo to można osiągnąć podobne zachowanie, implementując element DnsRoundRobinConnector , który ręcznie obraca się za pośrednictwem adresów IP zwracanych przez rozpoznawanie nazw DNS.

Nowy kod przy użyciu polecenia 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

Implementację DnsRoundRobinConnector można znaleźć tutaj.

DnsRoundRobinConnector Zwyczaj:

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

Przykład: Ustawianie właściwości SocketsHttpHandler

SocketsHttpHandler to zaawansowana i elastyczna procedura obsługi na platformie .NET, która udostępnia zaawansowane opcje konfiguracji do zarządzania połączeniami HTTP. Ustawiając różne właściwości socketsHttpHandler, można dostosować zachowanie klienta HTTP w celu spełnienia określonych wymagań, takich jak optymalizacja wydajności, ulepszenia zabezpieczeń i niestandardowa obsługa połączeń.

Oto przykład konfigurowania programu SocketsHttpHandler z różnymi właściwościami i używania go z klientem 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);

Przykład: Zmienianie personifikacjiLevel

Ta funkcja jest specyficzna dla niektórych platform i jest nieco nieaktualna. Jeśli potrzebujesz obejścia problemu, możesz zapoznać się z tą sekcją kodu.

Podczas pracy z HttpClientprogramem może być konieczne obsługiwanie certyfikatów klienta w różnych celach, takich jak niestandardowa walidacja certyfikatów serwera lub pobieranie certyfikatu serwera. HttpClient Udostępnia kilka właściwości i opcji efektywnego zarządzania certyfikatami.

Przykład: Sprawdzanie listy odwołania certyfikatów za pomocą programu SocketsHttpHandler

Właściwość CheckCertificateRevocationList w programie SocketsHttpHandler.SslOptions umożliwia deweloperom włączanie lub wyłączanie sprawdzania list odwołania certyfikatów (CRL) podczas uzgadniania protokołu SSL/TLS. Włączenie tej właściwości gwarantuje, że klient sprawdzi, czy certyfikat serwera został odwołany, zwiększając bezpieczeństwo połączenia.

Stary kod przy użyciu polecenia HttpWebRequest:

ServicePointManager.CheckCertificateRevocationList = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();

Nowy kod przy użyciu polecenia 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);

Przykład: pobieranie certyfikatu

Aby pobrać certyfikat z obiektu RemoteCertificateValidationCallback w HttpClientpliku , możesz użyć ServerCertificateCustomValidationCallback właściwości HttpClientHandler lub SocketsHttpHandler.SslOptions. To wywołanie zwrotne umożliwia inspekcję certyfikatu serwera podczas uzgadniania protokołu SSL/TLS.

Stary kod przy użyciu polecenia HttpWebRequest:

HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
X509Certificate? serverCertificate = request.ServicePoint.Certificate;

Nowy kod przy użyciu polecenia 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");

Przykład: Włączanie wzajemnego uwierzytelniania

Wzajemne uwierzytelnianie, nazywane również dwukierunkowym uwierzytelnianiem ssl lub certyfikatem klienta, jest procesem zabezpieczeń, w którym klient i serwer uwierzytelniają się nawzajem. Gwarantuje to, że obie strony są osobami, które twierdzą, zapewniając dodatkową warstwę zabezpieczeń dla poufnej komunikacji. W HttpClientprogramie można włączyć wzajemne uwierzytelnianie, konfigurując certyfikat klienta lub SocketsHttpHandler i weryfikując HttpClientHandler certyfikat serwera.

Aby włączyć wzajemne uwierzytelnianie, wykonaj następujące kroki:

  • Załaduj certyfikat klienta.
  • Skonfiguruj program HttpClientHandler lub SocketsHttpHandler, aby uwzględnić certyfikat klienta.
  • Skonfiguruj wywołanie zwrotne weryfikacji certyfikatu serwera, jeśli jest wymagana niestandardowa walidacja.

Oto przykład użycia programu 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);

Użycie właściwości nagłówka

Nagłówki odgrywają kluczową rolę w komunikacji HTTP, zapewniając podstawowe metadane dotyczące żądania i odpowiedzi. Podczas pracy z platformą HttpClient .NET można ustawić różne właściwości nagłówka i zarządzać nimi, aby kontrolować zachowanie żądań i odpowiedzi HTTP. Zrozumienie sposobu efektywnego korzystania z tych właściwości nagłówka może pomóc w zapewnieniu, że aplikacja komunikuje się wydajnie i bezpiecznie z usługami internetowymi.

Ustawianie nagłówków żądań

Nagłówki żądań są używane do przekazywania dodatkowych informacji do serwera o żądaniu. Typowe przypadki użycia obejmują określanie typu zawartości, ustawianie tokenów uwierzytelniania i dodawanie nagłówków niestandardowych. Nagłówki żądań można ustawić przy użyciu DefaultRequestHeaders właściwości HttpClient lub właściwości Headers obiektu HttpRequestMessage.

Przykład: Ustawianie niestandardowych nagłówków żądań

Ustawianie domyślnych niestandardowych nagłówków żądań w programie HttpClient

var client = new HttpClient();
client.DefaultRequestHeaders.Add("Custom-Header", "value");

Ustawianie niestandardowych nagłówków żądań w httpRequestMessage

var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Custom-Header", "value");

Przykład: Ustawianie typowych nagłówków żądań

Podczas pracy z platformą HttpRequestMessage .NET ustawienie typowych nagłówków żądań jest niezbędne do dostarczenia dodatkowych informacji na temat wykonywanego żądania. Te nagłówki mogą zawierać tokeny uwierzytelniania i nie tylko. Prawidłowe skonfigurowanie tych nagłówków gwarantuje, że żądania HTTP są prawidłowo przetwarzane przez serwer. Aby uzyskać kompleksową listę typowych właściwości dostępnych w programie HttpRequestHeaders, zobacz Właściwości.

Aby ustawić typowe nagłówki żądań w pliku HttpRequestMessage, możesz użyć Headers właściwości HttpRequestMessage obiektu . Ta właściwość zapewnia dostęp do HttpRequestHeaders kolekcji, w której można w razie potrzeby dodawać lub modyfikować nagłówki.

Ustawianie typowych domyślnych nagłówków żądań w programie HttpClient

using System.Net.Http.Headers;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "token");

Ustawianie typowych nagłówków żądań w httpRequestMessage

using System.Net.Http.Headers;

var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token");

Przykład: Ustawianie nagłówków zawartości

Nagłówki zawartości służą do dostarczania dodatkowych informacji o treści żądania HTTP lub odpowiedzi. Podczas pracy z HttpClient programem .NET można ustawić nagłówki zawartości, aby określić typ nośnika, kodowanie i inne metadane związane z wysyłaną lub odbieraną zawartością. Prawidłowe konfigurowanie nagłówków zawartości gwarantuje, że serwer i klient mogą poprawnie interpretować i przetwarzać zawartość.

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

Przykład: Ustaw wartość MaximumErrorResponseLength w obiekcie HttpClient

Użycie MaximumErrorResponseLength umożliwia deweloperom określenie maksymalnej długości zawartości odpowiedzi na błędy buforowania programu obsługi. Jest to przydatne do kontrolowania ilości danych odczytywanych i przechowywanych w pamięci po odebraniu odpowiedzi o błędzie z serwera. Korzystając z tej techniki, można zapobiec nadmiernemu użyciu pamięci i poprawić wydajność aplikacji podczas obsługi dużych odpowiedzi na błędy.

Istnieje kilka sposobów, aby to zrobić, przeanalizujemy TruncatedReadStream technikę w tym przykładzie:

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

Przykład użycia :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);
}

Przykład: Stosowanie nagłówków CachePolicy

Ostrzeżenie

HttpClient nie ma wbudowanej logiki do buforowania odpowiedzi. Nie ma obejścia innego niż implementowanie wszystkich buforowania samodzielnie. Po prostu ustawienie nagłówków nie spowoduje buforowania.

Podczas migracji z HttpWebRequest do HttpClientprogramu ważne jest, aby prawidłowo obsługiwać nagłówki związane z pamięcią podręczną, takie jak pragma i cache-control. Te nagłówki kontrolują sposób buforowania i pobierania odpowiedzi, zapewniając, że aplikacja zachowuje się zgodnie z oczekiwaniami w zakresie wydajności i aktualności danych.

W HttpWebRequestprogramie można użyć CachePolicy właściwości , aby ustawić te nagłówki. Jednak w pliku HttpClientnależy ręcznie ustawić te nagłówki na żądanie.

Stary kod przy użyciu polecenia HttpWebRequest:

HttpWebRequest request = WebRequest.CreateHttp(uri);
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();

W starszym HttpWebRequest interfejsie API stosowanie CachePolicy było proste ze względu na wbudowaną obsługę tej funkcji. Jednak nowszy HttpClient interfejs API nie zapewnia tej samej wbudowanej funkcjonalności. Mimo to można osiągnąć podobne zachowanie, implementując AddCacheControlHeaders ręcznie dodaj nagłówki powiązane z pamięcią podręczną.

Nowy kod przy użyciu polecenia HttpClient:

public static class CachePolicy
{
    public static void AddCacheControlHeaders(HttpRequestMessage request, RequestCachePolicy policy)

Implementację AddCacheControlHeaders można znaleźć tutaj.

AddCacheControlHeaders Zwyczaj:

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

Użycie właściwości buforowania

Podczas migracji z usługi HttpWebRequest do HttpClientusługi należy zrozumieć różnice w sposobie obsługi buforowania tych dwóch interfejsów API.

Stary kod przy użyciu polecenia HttpWebRequest:

W HttpWebRequestsystemie masz bezpośrednią kontrolę nad buforowaniem właściwości za pośrednictwem AllowWriteStreamBuffering właściwości i AllowReadStreamBuffering . Te właściwości włączają lub wyłączają buforowanie danych wysyłanych do serwera i odbieranych z serwera.

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AllowReadStreamBuffering = true; // Default is `false`.
request.AllowWriteStreamBuffering = false; // Default is `true`.

Nowy kod przy użyciu polecenia HttpClient:

W HttpClientsystemie nie ma bezpośrednich odpowiedników AllowWriteStreamBuffering właściwości i AllowReadStreamBuffering .

Obiekt HttpClient nie buforuje własnych treści żądań, zamiast tego deleguje odpowiedzialność za HttpContent użyte elementy. Zawartość, na przykład StringContent lub ByteArrayContent są już logicznie buforowane w pamięci, podczas gdy użycie StreamContent nie spowoduje domyślnego buforowania. Aby wymusić buforowaną zawartość, możesz wywołać HttpContent.LoadIntoBufferAsync metodę przed wysłaniem żądania. Oto przykład:

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

Buforowanie HttpClient odczytu jest domyślnie włączone. Aby tego uniknąć, możesz określić flagę HttpCompletionOption.ResponseHeadersRead lub użyć GetStreamAsync pomocnika.

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