Guida alla migrazione da HttpWebRequest a HttpClient
Questo articolo è stato pensato per assistere gli sviluppatori nel processo di migrazione da HttpWebRequest, ServicePoint e ServicePointManager a HttpClient. La migrazione è necessaria a causa dell'obsolescenza delle API precedenti e dei numerosi vantaggi offerti da HttpClient, tra cui prestazioni superiori, gestione delle risorse migliore e una progettazione API più moderna e flessibile. Seguendo i passaggi descritti in questo documento, gli sviluppatori potranno eseguire senza problemi la transizione delle codebase e usufruire di tutte le funzionalità fornite da HttpClient.
Avviso
La migrazione da HttpWebRequest
, ServicePoint
e ServicePointManager
a HttpClient
non ha solo lo scopo di migliorare le prestazioni. È fondamentale capire che le prestazioni della logica WebRequest
esistente potrebbero deteriorarsi in modo significativo dopo il passaggio a .NET (Core). Questo perché WebRequest
viene mantenuto a un livello di compatibilità minimo, ovvero privo di molte ottimizzazioni, ad esempio il riutilizzo delle connessioni in numerosi casi. Di conseguenza, la transizione a HttpClient
è essenziale per garantire che la gestione delle risorse e le prestazioni dell'applicazione siano conformi agli standard moderni.
Eseguire la migrazione da HttpWebRequest a HttpClient
Ecco alcuni esempi:
Richiesta GET semplice usando HttpWebRequest
Ecco un esempio di codice:
HttpWebRequest request = WebRequest.CreateHttp(uri);
using WebResponse response = await request.GetResponseAsync();
Richiesta GET semplice usando HttpClient
Ecco un esempio di codice:
HttpClient client = new();
using HttpResponseMessage message = await client.GetAsync(uri);
Richiesta POST semplice usando HttpWebRequest
Ecco un esempio di codice:
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();
Richiesta POST semplice usando HttpClient
Ecco un esempio di codice:
HttpClient client = new();
using HttpResponseMessage responseMessage = await client.PostAsync(uri, new StringContent("Hello World!"));
Guida alla migrazione da HttpWebRequest a HttpClient, SocketsHttpHandler
Migrare l'utilizzo di ServicePoint(Manager)
Si deve essere consapevoli che ServicePointManager
è una classe statica, ovvero tutte le modifiche apportate alle relative proprietà avranno un effetto globale su tutti gli oggetti ServicePoint
appena creati all'interno dell'applicazione. Ad esempio, quando si modifica una proprietà come ConnectionLimit
o Expect100Continue
, influirà su ogni nuova istanza di ServicePoint.
Avviso
In .NET moderno, HttpClient
non tiene conto delle configurazioni impostate in ServicePointManager
.
ServicePointManager mapping delle proprietà
Avviso
In .NET moderno, i valori predefiniti per le proprietà UseNagleAlgorithm
e Expect100Continue
sono impostati su false
. Per impostazione predefinita in .NET Framework questi valori sono true
.
ServicePointManager mapping dei metodi
ServicePointManager API precedente | Nuova API | Note |
---|---|---|
FindServicePoint |
Nessuna API equivalente | Soluzione alternativa non disponibile |
SetTcpKeepAlive |
Nessuna API equivalente diretta | Utilizzo di SocketsHttpHandler e ConnectCallback. |
ServicePoint mapping delle proprietà
ServicePoint API precedente | Nuova API | Note |
---|---|---|
Address |
HttpRequestMessage.RequestUri |
Si tratta dell'URI della richiesta. Queste informazioni sono disponibili in HttpRequestMessage . |
BindIPEndPointDelegate |
Nessuna API equivalente diretta | Utilizzo di SocketsHttpHandler e ConnectCallback. |
Certificate |
Nessuna API equivalente diretta | Queste informazioni possono essere recuperate da RemoteCertificateValidationCallback . Esempio: Recuperare il certificato. |
ClientCertificate |
Nessuna API equivalente | Esempio: Abilitazione dell'autenticazione reciproca. |
ConnectionLeaseTimeout |
SocketsHttpHandler.PooledConnectionLifetime |
Impostazione equivalente in HttpClient |
ConnectionLimit |
MaxConnectionsPerServer | Esempio: Impostazione delle proprietà SocketsHttpHandler. |
ConnectionName |
Nessuna API equivalente | Soluzione alternativa non disponibile |
CurrentConnections |
Nessuna API equivalente | Vedere Telemetria di rete in .NET. |
Expect100Continue |
ExpectContinue | Esempio: Impostare le intestazioni di richiesta. |
IdleSince |
Nessuna API equivalente | Soluzione alternativa non disponibile |
MaxIdleTime |
PooledConnectionIdleTimeout | Esempio: Impostazione delle proprietà SocketsHttpHandler. |
ProtocolVersion |
HttpRequestMessage.Version |
Esempio: Utilizzo delle proprietà HttpRequestMessage. |
ReceiveBufferSize |
Nessuna API equivalente diretta | Utilizzo di SocketsHttpHandler e ConnectCallback. |
SupportsPipelining |
Nessuna API equivalente | HttpClient non supporta il pipelining. |
UseNagleAlgorithm |
Nessuna API equivalente diretta | Utilizzo di SocketsHttpHandler e ConnectCallback. |
ServicePoint mapping dei metodi
ServicePoint API precedente | Nuova API | Note |
---|---|---|
CloseConnectionGroup |
Nessun equivalente | Soluzione alternativa non disponibile |
SetTcpKeepAlive |
Nessuna API equivalente diretta | Utilizzo di SocketsHttpHandler e ConnectCallback. |
Utilizzo delle proprietà HttpClient e HttpRequestMessage
Quando si usa HttpClient in .NET, è possibile accedere a un'ampia varietà di proprietà che consentono di configurare e personalizzare le richieste e le risposte HTTP. La comprensione di queste proprietà consente di usare al meglio HttpClient e di garantire che l'applicazione comunichi in modo efficiente e sicuro con i servizi Web.
Esempio: Utilizzo delle proprietà HttpRequestMessage
Ecco un esempio di come usare insieme HttpClient e 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`.
Esempio: Recuperare l'URI reindirizzato
Ecco un esempio di come recuperare l'URI reindirizzato (uguale a HttpWebRequest.Address
):
var client = new HttpClient();
using var response = await client.GetAsync(uri);
var redirectedUri = response.RequestMessage.RequestUri;
Utilizzo di SocketsHttpHandler e ConnectCallback
La proprietà ConnectCallback
in SocketsHttpHandler
consente agli sviluppatori di personalizzare il processo di connessione TCP. Può essere utile negli scenari in cui è necessario controllare la risoluzione DNS o applicare opzioni socket specifiche alla connessione. Usando ConnectCallback
, è possibile intercettare e modificare il processo di connessione prima che venga usato da HttpClient
.
Esempio: Associare l'indirizzo IP al socket
Nell'approccio precedente basato sull'uso di HttpWebRequest
, probabilmente si è usata la logica personalizzata per associare un indirizzo IP specifico a un socket. Ecco come ottenere una funzionalità simile usando HttpClient
e ConnectCallback
:
Codice precedente usando 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();
Nuovo codice usando HttpClient
e 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);
Esempio: Applicare opzioni socket specifiche
Se è necessario applicare opzioni socket specifiche, ad esempio l'abilitazione di TCP keep-alive, è possibile usare ConnectCallback
per configurare il socket prima che venga usato da HttpClient
. Di fatto, ConnectCallback
è più flessibile per configurare le opzioni socket.
Codice precedente usando 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();
Nuovo codice usando HttpClient
e 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);
Esempio: Abilitare il round robin DNS
Il round robine DNS è una tecnica usata per distribuire il traffico di rete tra più server ruotando su un elenco di indirizzi IP associati a un singolo nome di dominio. Questo favorisce il bilanciamento del carico e migliora la disponibilità dei servizi. Quando si usa HttpClient, è possibile implementare il round robin DNS gestendo manualmente la risoluzione DNS e ruotando gli indirizzi IP usando la proprietà ConnectCallback di SocketsHttpHandler.
Per abilitare il round robin DNS con HttpClient, è possibile usare la proprietà ConnectCallback per risolvere manualmente le voci DNS e ruotare gli indirizzi IP. Ecco un esempio per HttpWebRequest
e HttpClient
:
Codice precedente usando HttpWebRequest
:
ServicePointManager.DnsRefreshTimeout = 60000;
ServicePointManager.EnableDnsRoundRobin = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Nell'API HttpWebRequest
precedente, l'abilitazione del round robin DNS era semplice grazie al supporto predefinito per questa funzionalità. Tuttavia, la nuova API HttpClient
non fornisce la stessa funzionalità predefinita. Nonostante questo, è possibile ottenere un comportamento simile implementando un oggetto DnsRoundRobinConnector
che ruota manualmente gli indirizzi IP restituiti dalla risoluzione DNS.
Nuovo codice usando 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
È possibile trovare l'implementazione di DnsRoundRobinConnector
qui.
DnsRoundRobinConnector
Utilizzo:
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);
}
Esempio: Impostare le proprietà SocketsHttpHandler.
SocketsHttpHandler è un gestore potente e flessibile in .NET che offre opzioni di configurazione avanzate per la gestione delle connessioni HTTP. Impostando varie proprietà di SocketsHttpHandler, è possibile ottimizzare il comportamento del client HTTP in base a requisiti specifici, ad esempio l'ottimizzazione delle prestazioni, i miglioramenti alla sicurezza e la gestione della connessione personalizzata.
Ecco un esempio di come configurare SocketsHttpHandler con varie proprietà e come usarlo con 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);
Esempio: Modificare ImpersonationLevel
Questa funzionalità è specifica di alcune piattaforme e non è aggiornata. Se è necessaria una soluzione alternativa, è possibile fare riferimento a questa sezione del codice.
Utilizzo del certificato e delle proprietà correlate a TLS in HttpClient
Quando si usa HttpClient
, potrebbe essere necessario gestire i certificati client per vari scopi, ad esempio la convalida personalizzata dei certificati server o il recupero del certificato server. HttpClient
offre diverse proprietà e opzioni per gestire i certificati in modo efficace.
Esempio: Controllare l'elenco di revoche di certificati con SocketsHttpHandler
La proprietà CheckCertificateRevocationList
in SocketsHttpHandler.SslOptions
consente agli sviluppatori di abilitare o disabilitare il controllo degli elenchi di revoche di certificati (CRL) durante l'handshake SSL/TLS. Abilitando questa proprietà, il client verifica se il certificato del server è stato revocato, migliorando la sicurezza della connessione.
Codice precedente usando HttpWebRequest
:
ServicePointManager.CheckCertificateRevocationList = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Nuovo codice usando 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);
Esempio: Recuperare il certificato
Per recuperare il certificato da RemoteCertificateValidationCallback
in HttpClient
, è possibile usare la proprietà ServerCertificateCustomValidationCallback
di HttpClientHandler
o SocketsHttpHandler.SslOptions
. Questa funzione di richiamata consente di controllare il certificato del server durante l'handshake SSL/TLS.
Codice precedente usando HttpWebRequest
:
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
X509Certificate? serverCertificate = request.ServicePoint.Certificate;
Nuovo codice usando 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");
Esempio: Abilitare l'autenticazione reciproca
L'autenticazione reciproca, nota anche come autenticazione SSL bidirezionale o del certificato client, è un processo di sicurezza in cui il client e il server si autenticano l'uno con l'altro. In questo modo, viene autenticata l'identità di entrambe le parti, assicurando un livello di sicurezza aggiuntivo per le comunicazioni sensibili. In HttpClient
, è possibile abilitare l'autenticazione reciproca configurando HttpClientHandler
o SocketsHttpHandler
per includere il certificato client e convalidare il certificato server.
Per abilitare l'autenticazione reciproca, effettuare i seguenti passaggi:
- Caricare il certificato client.
- Configurare HttpClientHandler o SocketsHttpHandler per includere il certificato client.
- Impostare la funzione di richiamata della convalida del certificato server se è necessaria la convalida personalizzata.
Ecco un esempio di come usare 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);
Utilizzo delle proprietà di intestazione
Le intestazioni svolgono un ruolo fondamentale nella comunicazione HTTP, fornendo metadati essenziali sulla richiesta e la risposta. Quando si usa HttpClient
in .NET, è possibile impostare e gestire varie proprietà di intestazione per controllare il comportamento delle richieste e delle risposte HTTP. È fondamentale comprendere come usare queste proprietà di intestazione in modo efficace per garantire che l'applicazione comunichi in modo efficiente e sicuro con i servizi Web.
Impostare le intestazioni di richiesta
Le intestazioni di richiesta vengono usate per fornire informazioni aggiuntive al server sulla richiesta effettuata. I casi d'uso comuni includono la specifica del tipo di contenuto, l'impostazione dei token di autenticazione e l'aggiunta di intestazioni personalizzate. È possibile impostare le intestazioni di richiesta usando la proprietà DefaultRequestHeaders
di HttpClient
o la proprietà Headers di HttpRequestMessage
.
Esempio: Impostare intestazioni di richiesta personalizzate
Impostazione di intestazioni di richiesta personalizzate predefinite in HttpClient
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Custom-Header", "value");
Impostazione di intestazioni di richiesta personalizzate in HttpRequestMessage
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Custom-Header", "value");
Esempio: Impostare intestazioni di richiesta comuni
Quando si usa HttpRequestMessage
in .NET, l'impostazione di intestazioni di richiesta comuni è essenziale per fornire informazioni aggiuntive al server sulla richiesta effettuata. Queste intestazioni possono includere token di autenticazione e altro. La configurazione appropriata di queste intestazioni garantisce l'elaborazione corretta delle richieste HTTP da parte del server.
Per un elenco completo delle proprietà comuni disponibili in HttpRequestHeaders, vedere Proprietà.
Per impostare le intestazioni di richiesta comuni in HttpRequestMessage
, è possibile usare la Headers
proprietà dell'oggetto HttpRequestMessage
. Questa proprietà consente di accedere alla raccolta HttpRequestHeaders
, in cui è possibile aggiungere o modificare le intestazioni in base alle esigenze.
Impostazione di intestazioni di richiesta predefinite comuni in HttpClient
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "token");
Impostazione di intestazioni di richiesta comuni in HttpRequestMessage
using System.Net.Http.Headers;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token");
Esempio: Impostare le intestazioni di contenuto
Le intestazioni di contenuto vengono usate per fornire informazioni aggiuntive sul corpo di una richiesta o una risposta HTTP. Quando si usa HttpClient
in .NET, è possibile impostare intestazioni di contenuto per specificare il tipo di supporto, la codifica e altri metadati correlati al contenuto inviato o ricevuto. La configurazione appropriata delle intestazioni di contenuto garantisce che il server e il client possano interpretare ed elaborare correttamente il contenuto.
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);
Esempio: Impostare MaximumErrorResponseLength in HttpClient
L'utilizzo di MaximumErrorResponseLength
consente agli sviluppatori di specificare la lunghezza massima del contenuto della risposta di errore memorizzato nel buffer dal gestore. È utile per controllare la quantità di dati letti e archiviati in memoria quando viene ricevuta una risposta di errore dal server. Usando questa tecnica, è possibile evitare un utilizzo eccessivo di memoria e migliorare le prestazioni dell'applicazione quando si gestiscono risposte di errore di grandi dimensioni.
Esistono due modi per eseguire questa operazione. In questo esempio si esaminerà la tecnica TruncatedReadStream
:
internal sealed class TruncatedReadStream(Stream innerStream, long maxSize) : Stream
{
private long _maxRemainingLength = maxSize;
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override void Flush() => throw new NotSupportedException();
public override int Read(byte[] buffer, int offset, int count)
{
return Read(new Span<byte>(buffer, offset, count));
}
public override int Read(Span<byte> buffer)
{
int readBytes = innerStream.Read(buffer.Slice(0, (int)Math.Min(buffer.Length, _maxRemainingLength)));
_maxRemainingLength -= readBytes;
return readBytes;
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
}
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
int readBytes = await innerStream.ReadAsync(buffer.Slice(0, (int)Math.Min(buffer.Length, _maxRemainingLength)), cancellationToken)
.ConfigureAwait(false);
_maxRemainingLength -= readBytes;
return readBytes;
}
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
public override ValueTask DisposeAsync() => innerStream.DisposeAsync();
protected override void Dispose(bool disposing)
{
if (disposing)
{
innerStream.Dispose();
}
}
}
Esempio di utilizzo di 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);
}
Esempio: Applicare le intestazioni CachePolicy
Avviso
HttpClient
non dispone di logica predefinita per memorizzare nella cache le risposte. Non esiste altra soluzione alternativa che non sia l'implementazione della memorizzazione nella cache. La semplice impostazione delle intestazioni non comporta la memorizzazione nella cache.
Quando si esegue la migrazione da HttpWebRequest
a HttpClient
, è importante gestire correttamente le intestazioni correlate alla cache, ad esempio pragma
e cache-control
. Queste intestazioni controllano il modo in cui le risposte vengono memorizzate nella cache e recuperate, in modo che l'applicazione si comporti come previsto in termini di prestazioni e aggiornamento dei dati.
In HttpWebRequest
probabilmente si è usata la proprietà CachePolicy
per impostare queste intestazioni. Tuttavia, in HttpClient
è necessario impostare manualmente queste intestazioni nella richiesta.
Codice precedente usando HttpWebRequest
:
HttpWebRequest request = WebRequest.CreateHttp(uri);
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Nell'API HttpWebRequest
precedente, l'applicazione di CachePolicy
era semplice grazie al supporto predefinito per questa funzionalità. Tuttavia, la nuova API HttpClient
non fornisce la stessa funzionalità predefinita. Nonostante questo, è possibile ottenere un comportamento simile implementando un oggetto AddCacheControlHeaders
che aggiunge manualmente le intestazioni correlate alla cache.
Nuovo codice usando HttpClient
:
public static class CachePolicy
{
public static void AddCacheControlHeaders(HttpRequestMessage request, RequestCachePolicy policy)
È possibile trovare l'implementazione di AddCacheControlHeaders
qui.
AddCacheControlHeaders
Utilizzo:
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);
}
Utilizzo delle proprietà di buffering
Quando si esegue la migrazione da HttpWebRequest a HttpClient
, è importante comprendere le differenze nel modo in cui queste due API gestiscono il buffering.
Codice precedente usando HttpWebRequest
:
In HttpWebRequest
è possibile controllare direttamente le proprietà di buffering attraverso le proprietà AllowWriteStreamBuffering
e AllowReadStreamBuffering
. Queste proprietà consentono di abilitare o disabilitare il buffer dei dati inviati e ricevuti dal server.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AllowReadStreamBuffering = true; // Default is `false`.
request.AllowWriteStreamBuffering = false; // Default is `true`.
Nuovo codice usando HttpClient
:
In HttpClient
non esistono equivalenti diretti alle proprietà AllowWriteStreamBuffering
e AllowReadStreamBuffering
.
HttpClient non memorizza autonomamente nel buffer i corpi delle richieste, ma delega la responsabilità all'oggetto HttpContent
usato. Contenuti quali StringContent
o ByteArrayContent
sono già memorizzati logicamente nel buffer in memoria, mentre l'uso di StreamContent
non comporta alcun buffering per impostazione predefinita. Per forzare il buffering del contenuto, è possibile chiamare HttpContent.LoadIntoBufferAsync
prima di inviare la richiesta. Ecco un esempio:
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
il buffering in lettura è abilitato per impostazione predefinita. Per evitarlo, è possibile specificare il flag HttpCompletionOption.ResponseHeadersRead
oppure usare l'helper GetStreamAsync
.
HttpClient client = new HttpClient();
using HttpResponseMessage response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
await using Stream responseStream = await response.Content.ReadAsStreamAsync();
// Or simply
await using Stream responseStream = await client.GetStreamAsync(uri);