No envuelvas HttpClient en un using ── Comunicación HTTP en aplicaciones de negocio C# (patrones de creación, tiempos de espera y reintentos)

· · CSharp, .NET, HttpClient, IHttpClientFactory, Redes, async/await, Desarrollo Windows, Consultoría técnica

“A partir de media tarde, las conexiones a la API externa empiezan a fallar con SocketException”; “cambiamos el servidor de destino, pero la aplicación sigue conectándose al servidor antiguo” ── el HttpClient de C# es sencillo si solo se trata de llamar a GetAsync, pero si se comete un error en cómo crear y mantener la instancia, se termina introduciendo este tipo de fallo que “funciona en el momento, pero se rompe una vez en producción”.

En este artículo, partiendo del escenario en que una aplicación de negocio Windows llama a una API Web externa o a un servicio interno, repasamos el patrón correcto de creación de HttpClient, el diseño de tiempos de espera, los reintentos, el manejo de errores y, finalmente, los obstáculos propios del entorno Windows, en el orden en que suelen generar dudas en la práctica.

1. Primero, la conclusión (tabla de decisión)

La forma correcta de mantener HttpClient depende de la configuración de la aplicación. Primero lo resumimos en una tabla de decisión.

Tipo de aplicación Patrón recomendado Motivo
Herramienta de consola de vida corta (segundos a minutos, .NET) Un único HttpClient static/singleton En un proceso de corta duración, el problema de cambio de DNS es prácticamente irrelevante
Aplicación de larga duración / servicio Windows (.NET, sin DI) HttpClient static + configuración de SocketsHttpHandler.PooledConnectionLifetime Resuelve tanto el agotamiento de sockets como el problema de cambio de DNS
Aplicación con Generic Host / DI (.NET) IHttpClientFactory (AddHttpClient) La fábrica se encarga del pool y la rotación de handlers; los clientes con nombre/tipados permiten separar la configuración por destino
Aplicación .NET Framework Introducir IHttpClientFactory mediante el paquete Microsoft.Extensions.Http En .NET Framework, la creación manual agota puertos con más facilidad, y el uso de la fábrica también está recomendado oficialmente1
Cuando el proxy, las cookies o los certificados varían según el destino Separar un HttpClient por cada configuración (no reutilizarlo) La configuración de conexión del handler no puede cambiarse tras enviar la primera solicitud2

Dicho esto, adelantamos primero las conclusiones.

  • No debe hacer new HttpClient() en cada solicitud y destruirlo con using. HttpClient mantiene internamente un pool de conexiones y está diseñado para reutilizarse. Si se crea y destruye cada vez, se agotan los sockets disponibles bajo carga alta y se produce un SocketException2.
  • Pero tampoco basta con hacerlo static y ya está. HttpClient solo resuelve el DNS al crear la conexión, así que aunque cambie la dirección IP del destino, seguirá usando la conexión antigua. La solución recomendada oficialmente es limitar la vida de la conexión con SocketsHttpHandler.PooledConnectionLifetime1.
  • Si usa Generic Host o DI, déjelo en manos de IHttpClientFactory. La fábrica agrupa los handlers en un pool y los rota cada 2 minutos por defecto, lo que resuelve tanto el agotamiento de sockets como el cambio de DNS3.
  • El valor predeterminado del tiempo de espera es 100 segundos. Para una aplicación de negocio, esperar 100 segundos es prácticamente lo mismo que “quedarse colgada”, así que defina explícitamente el tiempo de espera según los requisitos de cada destino.
  • No implemente los reintentos por su cuenta; empiece por el handler estándar de Microsoft.Extensions.Http.Resilience. Permite introducir reintentos, disyuntor (circuit breaker) y tiempos de espera con un conjunto de valores predeterminados ya probados, evitando desde el diseño accidentes habituales en los bucles de reintento caseros, como reenviar sin condiciones un POST fallido y provocar un registro duplicado4.

2. Por qué “crear con using en cada solicitud” es un error ── agotamiento de sockets

Como HttpClient implementa IDisposable, un código como el siguiente parece correcto a primera vista.

// Antipatrón: crear y destruir en cada solicitud
public async Task<string> GetDataAsync(string url)
{
    using var client = new HttpClient();
    return await client.GetStringAsync(url);
}

El problema es que, aunque se llame a Dispose, el socket no se libera de inmediato a nivel del sistema operativo. Según la especificación TCP, el socket del lado que cierra la conexión permanece un tiempo en estado TIME_WAIT. Mientras la frecuencia de llamadas sea baja no ocurre nada, pero cuando la carga aumenta, los sockets sin liberar se van acumulando y, en algún momento, deja de poder conectarse de repente con un SocketException2.

Lo complicado de este fallo es que prácticamente nunca se reproduce durante el desarrollo o las pruebas. Se manifiesta en forma de caídas que solo ocurren en las horas de mayor carga en producción, o solo durante el proceso por lotes de fin de mes. Para investigarlo, cuando se produzca el problema, comprobar el número de sockets en TIME_WAIT con el siguiente comando ayuda a orientar la causa.

# Contar el número de sockets en TIME_WAIT por destino de conexión
netstat -ano | Select-String "TIME_WAIT" | Measure-Object

Cabe señalar que el HttpClient obtenido a través de IHttpClientFactory queda fuera de este problema. En un cliente creado por la fábrica, el handler (la entidad real del pool de conexiones) no se destruye aunque se llame a Dispose, por lo que envolverlo en un using es seguro3.

3. Hacerlo static no es el final de la historia ── el problema del cambio de DNS

Convertir HttpClient en static como medida contra el agotamiento de sockets va en la dirección correcta, pero por sí solo deja otro problema sin resolver. HttpClient solo resuelve el DNS al crear la conexión, y no tiene en cuenta el TTL de los registros DNS2. Mientras la conexión siga viva dentro del pool, seguirá conectándose a la IP antigua aunque la dirección IP del destino cambie.

El fallo típico de “cambiamos el DNS en un failover, pero la aplicación siguió apuntando al servidor antiguo hasta reiniciarla” tiene su origen en este mecanismo. La solución recomendada por las guías oficiales es limitar la vida de la conexión con SocketsHttpHandler.PooledConnectionLifetime1.

// Patrón recomendado en .NET (Core) / .NET 5+:
// hacer que la conexión se recree periódicamente para seguir los cambios de DNS
private static readonly HttpClient SharedClient = new(new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(2)
});

Cuando una conexión llega al final de su vida útil, se recrea en la siguiente solicitud, momento en el que se vuelve a resolver el DNS. El valor se decide en función de la rapidez con la que se desee seguir los cambios de DNS. El ejemplo de la documentación oficial usa 2 minutos, pero en un sistema interno cuyo destino rara vez cambia, un valor más largo tampoco supone ningún problema1.

Cabe señalar que SocketsHttpHandler es una implementación de .NET Core 2.1 en adelante y no está disponible en .NET Framework. En ese caso, use IHttpClientFactory, tratado en el siguiente capítulo1.

4. Si usa DI, recurra a IHttpClientFactory

En aplicaciones que usan Generic Host o un contenedor de DI, IHttpClientFactory (AddHttpClient) es la primera opción a considerar. Para una explicación del propio Generic Host, consulte “Qué es el Generic Host”; para su introducción en aplicaciones de escritorio, consulte “Usar Generic Host + BackgroundService en una aplicación de escritorio”.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

// Cliente con nombre: separa la configuración por destino de conexión
builder.Services.AddHttpClient("OrderApi", client =>
{
    client.BaseAddress = new Uri("https://order.example.co.jp/");
    client.Timeout = TimeSpan.FromSeconds(10);
});

Hay tres puntos clave que conviene tener presentes sobre el comportamiento de la fábrica.

  • Los handlers se agrupan en un pool y se rotan cada 2 minutos por defecto. Cada llamada a CreateClient devuelve un nuevo HttpClient, pero el handler subyacente (el pool de conexiones) se comparte, por lo que no se agotan los sockets, y la rotación periódica también permite seguir los cambios de DNS3.
  • Se parte de la base de que el HttpClient creado por la fábrica se usa de forma efímera. Si se guarda la instancia obtenida en un campo singleton, deja de participar en la rotación de handlers y no seguirá los cambios de DNS. Por el mismo motivo, evite también inyectar un cliente tipado en un servicio singleton3.
  • Hay que tener cuidado en aplicaciones que dependen de cookies. Como resultado de compartir el pool de handlers, el CookieContainer se comparte de forma no intencionada. Si la aplicación usa cookies, la documentación oficial recomienda evitar la fábrica o desactivar el manejo de cookies y añadir las cabeceras manualmente1.

Para la obtención de tokens al llamar a APIs con autenticación (por ejemplo, APIs protegidas por Microsoft Entra ID), consulte “Integrar la autenticación de Entra ID en una aplicación WinForms/WPF”, donde se trata este tema.

5. Diseño de tiempos de espera ── los 100 segundos predeterminados son demasiado para una aplicación de negocio

El valor predeterminado de HttpClient.Timeout es 100 segundos5. En una aplicación de negocio que llama a una API como extensión de una operación de pantalla, hacer esperar 100 segundos equivale a que la aplicación parezca “congelada”, así que conviene configurarlo explícitamente para cada destino.

// Tiempo de espera predeterminado para todo el cliente
client.Timeout = TimeSpan.FromSeconds(10);

// Si se desea acortar/alargar solo una solicitud concreta, use CancellationTokenSource
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
HttpResponseMessage response = await client.GetAsync(url, cts.Token);

Los puntos a tener en cuenta en el diseño son los siguientes.

  • La excepción en caso de tiempo de espera es TaskCanceledException. Desde .NET 5, en los tiempos de espera provocados por HttpClient.Timeout, la excepción interna contiene un TimeoutException5. Sin embargo, en un tiempo de espera propio como el del CancellationTokenSource anterior, no se adjunta ninguna excepción interna. La forma fiable de distinguir entre un tiempo de espera y una cancelación provocada por el usuario no es mirar la excepción interna, sino comprobar si el token pasado por el llamador ya está cancelado (véase el ejemplo de código del capítulo 7). Tenga cuidado, porque si en el bloque catch solo se captura HttpRequestException, se pasará por alto el tiempo de espera.
  • Timeout limita “la solicitud completa”. Si se quiere limitar únicamente el establecimiento de la conexión con un valor más corto, se combina con SocketsHttpHandler.ConnectTimeout. Un requisito como “si el servidor está caído, renunciar en 3 segundos, pero esperar hasta 60 segundos para una respuesta grande en condiciones normales” se puede expresar combinando ambos ajustes2.
  • Para la descarga de archivos grandes, evite el almacenamiento en búfer predeterminado. HttpClient carga por defecto toda la respuesta en memoria, así que para descargas de decenas de MB o más, especifique HttpCompletionOption.ResponseHeadersRead y procese la respuesta como un stream2.

Para la configuración que extrae los valores de tiempo de espera a appsettings.json y los varía según el entorno, se puede aplicar tal cual la tabla de decisión de “Gestión práctica de configuración en aplicaciones de negocio Windows”.

Cabe señalar que, al llamar a comunicación HTTP desde WinForms/WPF, bloquear con .Result o .Wait() provoca un interbloqueo (deadlock) en el hilo de la interfaz de usuario. Esta trampa clásica se explica en detalle en “Tabla de decisión práctica de C# async/await”, así que se recomienda leerlo antes de escribir código de comunicación.

6. Reintentos ── use el handler de resilience estándar en lugar de un bucle propio

Como la red falla de forma temporal, el código que llama a una API externa necesita reintentos. Sin embargo, un reintento casero basado en un bucle for y Task.Delay obliga a implementar correctamente por su cuenta todos los puntos siguientes, y no compensa el esfuerzo.

  • Distinguir entre fallos que admiten reintento (tiempo de espera, HTTP 408/429/5xx) y fallos en los que reintentar es inútil (HTTP 400/401/404)
  • Excluir los métodos HTTP en los que reejecutar provoca un accidente (por ejemplo, la ejecución duplicada de un registro mediante POST)
  • Retroceso exponencial y jitter (la variación aleatoria que evita que todos los clientes reenvíen a la vez y vuelvan a tumbar el servidor)
  • Un disyuntor (circuit breaker) que detenga las propias llamadas cuando el fallo persiste

El handler estándar del paquete Microsoft.Extensions.Http.Resilience proporciona todo este conjunto con valores predeterminados ya probados en producción4.

builder.Services.AddHttpClient("OrderApi", client =>
    {
        client.BaseAddress = new Uri("https://order.example.co.jp/");
    })
    .AddStandardResilienceHandler(); // Conjunto estándar de reintentos + disyuntor + tiempos de espera

Los valores predeterminados del handler estándar son: 30 segundos de tiempo de espera para toda la solicitud, hasta 3 reintentos con retroceso exponencial (retraso inicial de 2 segundos, con jitter), 10 segundos de tiempo de espera por intento, y una configuración que trata HTTP 408/429/5xx y HttpRequestException como errores temporales4.

Solo hay un punto a vigilar en los valores predeterminados: el handler estándar reintenta por defecto todos los métodos HTTP. En APIs donde una ejecución duplicada de un POST de registro sea problemática, desactive el reintento de los métodos no seguros4.

httpClientBuilder.AddStandardResilienceHandler(options =>
{
    // Desactivar la reejecución de POST/PUT/DELETE, etc.
    options.Retry.DisableForUnsafeHttpMethods();
});

Cabe señalar que los reintentos solo resuelven fallos temporales. Síntomas como que la conexión esté establecida pero no fluyan datos, o que la respuesta sea extremadamente lenta, suelen ser en realidad un problema de la capa TCP, y para aislar la causa puede resultar útil el método tratado en “Retransmisiones TCP y RFC1323 ── investigación de una parada de comunicación en una cámara industrial”.

7. Manejo de errores ── cómo tratar los códigos de estado

HttpClient no lanza una excepción ante fallos como HTTP 404 o 500, en los que sí se recibió una respuesta HTTP. Las excepciones se producen cuando no se obtiene respuesta alguna: fallo de conexión, tiempo de espera, cancelación, etc. Conviene escribir el código distinguiendo conscientemente estas dos categorías.

try
{
    using HttpResponseMessage response = await client.GetAsync(url, ct);

    if (!response.IsSuccessStatusCode)
    {
        // Se recibió respuesta pero fue un fallo: se puede distinguir por el código de estado
        if (response.StatusCode == HttpStatusCode.NotFound)
        {
            return null; // Ejemplo de tratar "no existe" como caso normal
        }
        response.EnsureSuccessStatusCode(); // El resto se convierte en HttpRequestException
    }

    return await response.Content.ReadFromJsonAsync<Order>(ct);
}
catch (HttpRequestException ex)
{
    // Fallo de conexión, o estado de fallo generado por EnsureSuccessStatusCode.
    // Desde .NET 5, se puede consultar el código de estado del fallo con ex.StatusCode
    logger.LogError(ex, "Error al llamar a la API de pedidos. StatusCode={StatusCode}", ex.StatusCode);
    throw;
}
catch (TaskCanceledException) when (ct.IsCancellationRequested)
{
    // Cancelación provocada por el token pasado por el llamador (por ejemplo, se cerró la pantalla).
    // No es un error, así que se propaga tal cual sin ensuciar el log
    throw;
}
catch (TaskCanceledException ex)
{
    // Expiración de HttpClient.Timeout, o del CTS propio usado para el tiempo de espera
    logger.LogError(ex, "Se agotó el tiempo de espera al llamar a la API de pedidos");
    throw;
}

Decisiones como “tratar un 404 como excepción o como null” dependen de la semántica de la API de destino. Si se convierte todo en excepción de un plumazo con EnsureSuccessStatusCode, el bloque catch del lado llamador termina hinchándose. El criterio para decidir qué se convierte en excepción y qué se expresa mediante el valor de retorno se puede aplicar tal cual desde “La práctica de capturar excepciones, registrar logs y manejar errores”.

Para el envío y recepción de JSON, usar GetFromJsonAsync / PostAsJsonAsync / ReadFromJsonAsync de System.Net.Http.Json evita tener que escribir a mano la serialización a través de cadenas de texto.

8. Obstáculos propios de las aplicaciones de negocio Windows

Por último, resumimos los obstáculos que se suelen encontrar en la práctica en entornos Windows.

  • La detección automática de proxy hace lenta la primera solicitud. Por defecto en Windows, HttpClient usa la configuración de proxy del sistema operativo (incluida la detección automática). Si se sabe que no se necesita proxy, desactivarlo con HttpClientHandler.UseProxy = false elimina la espera de la detección2. Por el contrario, en entornos donde el proxy corporativo es obligatorio, especificarlo explícitamente con WebProxy evita el problema de “funciona en la máquina de desarrollo pero no en el servidor”.
  • Complete la configuración del proxy antes de la primera solicitud. Los ajustes de conexión del handler no surten efecto si se cambian después de haber enviado ya una solicitud2.
  • El valor predeterminado del número de conexiones simultáneas es opuesto entre .NET y .NET Framework. En .NET (SocketsHttpHandler), el número de conexiones simultáneas de HTTP/1.1 es ilimitado por defecto, por lo que con un gran volumen de solicitudes en paralelo las conexiones siguen aumentando y pueden chocar con los límites del firewall o del servidor. En procesos con alto grado de paralelismo, establezca un límite con MaxConnectionsPerServer2. Por el contrario, en .NET Framework el valor predeterminado de ServicePointManager.DefaultConnectionLimit es pequeño (2 en entornos que no son ASP.NET), lo que provoca el problema opuesto: las solicitudes en paralelo quedan a la espera internamente y acaban agotando el tiempo de espera. Si necesita aumentar el paralelismo en .NET Framework, eleve explícitamente este límite6.
  • Al llamar desde un servicio Windows, el contexto de proxy y TLS difiere del de un usuario. La cuenta con la que se ejecuta el servicio no tiene la configuración de proxy ni las credenciales del usuario, lo que es una causa habitual del típico fallo de comunicación “funciona con un usuario interactivo pero no en el servicio”. Para el contexto de ejecución específico de los servicios, consulte “Cómo crear y operar servicios Windows”.
  • No incruste en el código la URL de destino ni las claves de API. Delegue el cambio de destino a un archivo de configuración (“Gestión práctica de configuración”) y el almacenamiento de información sensible al método descrito en “Evitar configuraciones en texto plano con DPAPI”.

Resumen

En la práctica, la calidad de HttpClient se decide más por “cómo se mantiene” que por “cómo se llama”. Crear una instancia por solicitud provoca agotamiento de sockets, y convertirlo ingenuamente en static provoca que no se siga el cambio de DNS; ninguno de los dos problemas es visible durante el desarrollo. La respuesta es una instancia compartida con PooledConnectionLifetime o IHttpClientFactory en .NET, e introducir IHttpClientFactory en .NET Framework. Además, hay que definir explícitamente el tiempo de espera para cada destino y dejar los reintentos en manos del handler de resilience estándar; solo entonces la aplicación de negocio queda preparada para el supuesto de “una red que falla de vez en cuando”.

Revisar la comunicación de una aplicación existente (caídas que solo ocurren en horas punta, reorganización del diseño de tiempos de espera, nuevas implementaciones de integración con APIs externas) suele requerir un juicio basado en el código real y en el entorno operativo, así que si tiene dudas, no dude en consultarnos.

Artículos relacionados

Áreas de consultoría relacionadas

Komura Soft, LLC se encarga del desarrollo de aplicaciones de negocio Windows, incluida la integración con APIs externas, así como de la consultoría técnica para investigar problemas de comunicación en aplicaciones existentes (agotamiento de sockets, tiempos de espera, fallos de conexión intermitentes) y definir la estrategia de corrección.

Referencias

  1. Microsoft Learn, Pautas para el uso de HttpClient. Sobre el uso, en .NET Core/.NET 5+, de un cliente de larga duración con PooledConnectionLifetime configurado o de un cliente de corta duración creado por IHttpClientFactory; sobre la recomendación de usar IHttpClientFactory en .NET Framework; y sobre por qué las aplicaciones que usan cookies deberían evitar IHttpClientFactory debido a que se comparte el CookieContainer 2 3 4 5 6

  2. Microsoft Learn, Clase HttpClient. Sobre cómo crear una instancia por cada solicitud provoca agotamiento de sockets y SocketException; sobre que el DNS solo se resuelve al crear la conexión y no se tiene en cuenta el TTL; sobre que la configuración de conexión del handler no puede cambiarse después de la primera solicitud; sobre que el número de conexiones simultáneas de HTTP/1.1 es ilimitado por defecto; sobre la recomendación de usar streaming para descargas grandes; y sobre el comportamiento predeterminado del proxy y su desactivación mediante UseProxy 2 3 4 5 6 7 8 9

  3. Microsoft Learn, IHttpClientFactory con .NET. Sobre que la vida útil predeterminada del handler es de 2 minutos; sobre que se parte de la base de que el HttpClient creado por la fábrica se usa de forma efímera; sobre que llamar a Dispose en un cliente creado por la fábrica no destruye el handler; y sobre que inyectar un cliente tipado en un singleton hace que deje de seguir los cambios de DNS.  2 3 4

  4. Microsoft Learn, Compilar aplicaciones HTTP resilientes: patrones de desarrollo clave. Sobre las 5 estrategias que configura AddStandardResilienceHandler (limitador de tasa / tiempo de espera total de 30 segundos / hasta 3 reintentos con retroceso exponencial / disyuntor / tiempo de espera de 10 segundos por intento), los códigos de estado (408/429/5xx) y excepciones cubiertos, y la desactivación del reintento de POST y similares mediante DisableForUnsafeHttpMethods 2 3 4

  5. Microsoft Learn, Propiedad HttpClient.Timeout. Sobre que el valor predeterminado es de 100 segundos, y sobre que, desde .NET 5, al producirse un tiempo de espera se lanza un TaskCanceledException con un TimeoutException como excepción interna.  2

  6. Microsoft Learn, Propiedad ServicePointManager.DefaultConnectionLimit. Sobre que el número predeterminado de conexiones simultáneas es 10 en aplicaciones alojadas en ASP.NET, y 2 en el resto de casos (aplicaciones de escritorio, etc.). 

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

HttpClient implementa IDisposable, ¿pero no se debe envolver en un using?
El problema es el patrón de "crear y destruir en cada solicitud". HttpClient mantiene internamente un pool de conexiones y está diseñado para reutilizarse durante toda la vida de la aplicación. Si se crea y destruye cada vez, los sockets permanecen un tiempo en estado TIME_WAIT incluso después de liberarse, de modo que con carga alta se agotan los sockets disponibles y se produce un SocketException. Destrúyalo una sola vez al finalizar la aplicación, u obténgalo a través de IHttpClientFactory. El HttpClient obtenido mediante la fábrica no destruye el handler aunque se llame a Dispose, por lo que envolverlo en un using no supone ningún problema.
¿Debería migrar desde WebClient o HttpWebRequest de .NET Framework?
Se recomienda unificar el código nuevo en torno a HttpClient. WebClient y HttpWebRequest son APIs antiguas que se mantienen por compatibilidad, y Microsoft también recomienda usar HttpClient en el desarrollo nuevo. No es necesario reemplazar todo el código existente de golpe, pero ir migrando a HttpClient cada vez que se toque la parte de comunicación facilita el mantenimiento en cuanto a control de tiempos de espera, soporte de async y facilidad de pruebas.
¿Cuántas veces y con qué intervalo se deberían hacer los reintentos?
Es más seguro partir de los valores predeterminados del handler estándar de Microsoft.Extensions.Http.Resilience (máximo 3 intentos, retroceso exponencial con jitter, retraso inicial de 2 segundos) que decidirlo por cuenta propia. Lo importante no es tanto el número de intentos como determinar si la solicitud admite reintentos: para operaciones como un POST, donde reejecutarla puede provocar un registro duplicado, desactive el reintento por defecto o active los reintentos solo después de garantizar la idempotencia en el servidor (la propiedad de que el resultado no cambie aunque la misma solicitud se reciba dos veces).
¿Por qué solo la primera solicitud es extremadamente lenta en un entorno con proxy corporativo?
Con la configuración predeterminada de Windows, HttpClient intenta detectar automáticamente el proxy, por lo que la primera conexión puede tardar debido al proceso de detección. En entornos donde se sabe que no se necesita proxy (por ejemplo, comunicación interna entre servidores), desactivar la detección automática poniendo UseProxy en false en HttpClientHandler mejora el comportamiento. Por el contrario, en entornos corporativos donde el proxy es obligatorio, es más estable especificarlo explícitamente con WebProxy en lugar de depender de la detección automática.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog