Compartir a través de


Realización de solicitudes HTTP con la clase HttpClient

En este artículo, aprenderá a realizar solicitudes HTTP y a controlar las respuestas con la clase HttpClient.

Importante

Todas las solicitudes HTTP de ejemplo se dirigen a una de las siguientes direcciones URL:

Normalmente, los puntos de conexión HTTP devuelven datos de notación de objetos JavaScript (JSON), pero no siempre. Por comodidad, el paquete NuGet System.Net.Http.Json opcional proporciona varios métodos de extensión para HttpClient y HttpContent que realizan la serialización y deserialización automáticas mediante System.Text.Json. Los ejemplos siguientes destacan las ubicaciones donde estas extensiones están disponibles.

Sugerencia

Todo el código fuente de este artículo está disponible en el repositorio GitHub: Documentos de .NET.

Cree una clave privada RSA HttpClient.

La mayoría de los ejemplos siguientes reutilizan la misma instancia HttpClient y, por tanto, solo deben configurarse una vez. Para crear un HttpClient, use el constructor de clase HttpClient. Para obtener más información, consulte Directrices para usar HttpClient.

// HttpClient lifecycle management best practices:
// https://learn.microsoft.com/dotnet/fundamentals/networking/http/httpclient-guidelines#recommended-use
private static HttpClient sharedClient = new()
{
    BaseAddress = new Uri("https://jsonplaceholder.typicode.com"),
};

El código anterior:

  • Crea una instancia de una nueva instancia HttpClient como una variable static. Según las directrices, se recomienda reutilizar instancias HttpClient durante el ciclo de vida de la aplicación.
  • Establece HttpClient.BaseAddress en "https://jsonplaceholder.typicode.com".

Esta instancia de HttpClient usa la dirección base al realizar las solicitudes posteriores. Para aplicar otra configuración, tenga en cuenta lo siguiente:

Sugerencia

Como alternativa, puede crear instancias HttpClient mediante un enfoque de patrón de fábrica que le permita configurar cualquier número de clientes y consumirlos como servicios de inserción de dependencias. Para obtener más información, consulte fábrica de cliente HTTP con .NET.

Realización de una solicitud HTTP

Para realizar una solicitud HTTP, llame a cualquiera de las siguientes API:

HTTP method API
GET HttpClient.GetAsync
GET HttpClient.GetByteArrayAsync
GET HttpClient.GetStreamAsync
GET HttpClient.GetStringAsync
POST HttpClient.PostAsync
PUT HttpClient.PutAsync
PATCH HttpClient.PatchAsync
DELETE HttpClient.DeleteAsync
USER SPECIFIED HttpClient.SendAsync

Una solicitud USER SPECIFIED indica que el método SendAsync acepta cualquier HttpMethod válido.

Advertencia

La realización de solicitudes HTTP se consideran tareas enlazadas a E/S de red. Aunque hay un método HttpClient.Send sincrónico, en su lugar, se recomienda usar las API asincrónicas, a menos que tenga una buena razón para no hacerlo.

Nota:

Aunque va dirigido a dispositivos Android (como con el desarrollo de MAUI de .NET), debe agregar android:usesCleartextTraffic="true" a <application></application> en AndroidManifest.xml. Esto habilita el tráfico de texto no cifrado, como las solicitudes HTTP, que de otro modo se deshabilitan de forma predeterminada debido a las directivas de seguridad de Android. Considere la siguiente configuración XML de ejemplo:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:usesCleartextTraffic="true"></application>
  <!-- omitted for brevity -->
</manifest>

Para obtener más información, consulte Habilitar tráfico de red de texto no cifrado para el dominio localhost.

Contenido HTTP

El tipo HttpContent se usa para representar un cuerpo de entidad HTTP y los encabezados de contenido correspondientes. Para los métodos HTTP o métodos de solicitud que requieren un cuerpo, POST, PUT y PATCH, use la clase HttpContent para especificar el cuerpo de la solicitud. La mayoría de los ejemplos muestran cómo preparar la subclase StringContent con una carga JSON, pero existen otras subclases para diferentes tipos de contenido (MIME).

  • ByteArrayContent: proporciona contenido HTTP basado en una matriz de bytes.
  • FormUrlEncodedContent: proporciona contenido HTTP para tuplas de nombre y valor codificadas mediante el tipo MIME "application/x-www-form-urlencoded".
  • JsonContent: proporciona contenido HTTP basado en JSON.
  • MultipartContent: proporciona una colección de objetos HttpContent que se serializan con la especificación del tipo MIME "multipart/*".
  • MultipartFormDataContent: proporciona un contenedor para el contenido codificado con el tipo MIME "multipart/form-data".
  • ReadOnlyMemoryContent: proporciona contenido HTTP basado en un elemento ReadOnlyMemory<T>.
  • StreamContent: proporciona contenido HTTP basado en una secuencia.
  • StringContent: proporciona contenido HTTP basado en una cadena.

La clase HttpContent también se usa para representar el cuerpo de respuesta de HttpResponseMessage, al que se puede acceder en la propiedad HttpResponseMessage.Content.

HTTP Get

Una solicitud GET no debe enviar un cuerpo y se usa (como indica el nombre del método) para recuperar u obtener datos de un recurso. Para realizar una solicitud HTTP GET, con un HttpClient y un URI, use el método HttpClient.GetAsync:

static async Task GetAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.GetAsync("todos/3");
    
    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 3,
    //     "title": "fugiat veniam minus",
    //     "completed": false
    //   }
}

El código anterior:

  • Realiza una solicitud GET a "https://jsonplaceholder.typicode.com/todos/3".
  • Garantiza que la respuesta es correcta.
  • Escribe los detalles de la solicitud en la consola.
  • Lee el cuerpo de la respuesta como una cadena.
  • Escribe el cuerpo de la respuesta JSON en la consola.

WriteRequestToConsole es un método de extensión personalizado que no forma parte del marco de trabajo, pero si tiene curiosidad por saber cómo se implementa, tenga en cuenta el siguiente código de C#:

static class HttpResponseMessageExtensions
{
    internal static void WriteRequestToConsole(this HttpResponseMessage response)
    {
        if (response is null)
        {
            return;
        }

        var request = response.RequestMessage;
        Console.Write($"{request?.Method} ");
        Console.Write($"{request?.RequestUri} ");
        Console.WriteLine($"HTTP/{request?.Version}");        
    }
}

Esta funcionalidad se usa para escribir los detalles de la solicitud en la consola de la siguiente forma:

<HTTP Request Method> <Request URI> <HTTP/Version>

Por ejemplo, la solicitud GET para https://jsonplaceholder.typicode.com/todos/3 genera el mensaje siguiente:

GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1

HTTP Get de JSON

El punto de conexión https://jsonplaceholder.typicode.com/todos devuelve una matriz JSON de objetos "todo". Su estructura JSON es similar a la siguiente:

[
  {
    "userId": 1,
    "id": 1,
    "title": "example title",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "another example title",
    "completed": true
  },
]

El objeto Todo de C# se define del modo siguiente:

public record class Todo(
    int? UserId = null,
    int? Id = null,
    string? Title = null,
    bool? Completed = null);

Es un tipo record class, con las propiedades opcionales Id, Title, Completed y UserId. Para obtener más información sobre el tipo record, vea Introducción a los tipos de registro en C#. Para deserializar automáticamente las solicitudes GET en un objeto C# fuertemente tipado, use el método de extensión GetFromJsonAsync que forma parte del paquete NuGet System.Net.Http.Json.

static async Task GetFromJsonAsync(HttpClient httpClient)
{
    var todos = await httpClient.GetFromJsonAsync<List<Todo>>(
        "todos?userId=1&completed=false");

    Console.WriteLine("GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1");
    todos?.ForEach(Console.WriteLine);
    Console.WriteLine();

    // Expected output:
    //   GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1
    //   Todo { UserId = 1, Id = 1, Title = delectus aut autem, Completed = False }
    //   Todo { UserId = 1, Id = 2, Title = quis ut nam facilis et officia qui, Completed = False }
    //   Todo { UserId = 1, Id = 3, Title = fugiat veniam minus, Completed = False }
    //   Todo { UserId = 1, Id = 5, Title = laboriosam mollitia et enim quasi adipisci quia provident illum, Completed = False }
    //   Todo { UserId = 1, Id = 6, Title = qui ullam ratione quibusdam voluptatem quia omnis, Completed = False }
    //   Todo { UserId = 1, Id = 7, Title = illo expedita consequatur quia in, Completed = False }
    //   Todo { UserId = 1, Id = 9, Title = molestiae perspiciatis ipsa, Completed = False }
    //   Todo { UserId = 1, Id = 13, Title = et doloremque nulla, Completed = False }
    //   Todo { UserId = 1, Id = 18, Title = dolorum est consequatur ea mollitia in culpa, Completed = False }
}

En el código anterior:

  • Se realiza una solicitud GET a "https://jsonplaceholder.typicode.com/todos?userId=1&completed=false".
    • La cadena de consulta representa los criterios de filtrado de la solicitud.
  • La respuesta se deserializa automáticamente en un objeto List<Todo> cuando se ejecuta correctamente.
  • Los detalles de la solicitud se escriben en la consola, junto con cada objeto Todo.

HTTP Post

Una solicitud POST envía datos al servidor para su procesamiento. El encabezado Content-Type de la solicitud indica qué tipo MIME envía el cuerpo. Para realizar una solicitud HTTP POST, con un HttpClient y un Uri, use el método HttpClient.PostAsync:

static async Task PostAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new
        {
            userId = 77,
            id = 1,
            title = "write code sample",
            completed = false
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PostAsync(
        "todos",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
    //   {
    //     "userId": 77,
    //     "id": 201,
    //     "title": "write code sample",
    //     "completed": false
    //   }
}

El código anterior:

  • Prepara una instancia StringContent con el cuerpo JSON de la solicitud (tipo MIME de "application/json").
  • Realiza una solicitud POST a "https://jsonplaceholder.typicode.com/todos".
  • Garantiza que la respuesta es correcta y escribe los detalles de la solicitud en la consola.
  • Escribe el cuerpo de la respuesta como una cadena en la consola.

HTTP Post como JSON

Para serializar automáticamente los argumentos de solicitud POST y deserializar las respuestas en objetos C# fuertemente tipados, use el método de extensión PostAsJsonAsync que forma parte del paquete NuGet System.Net.Http.Json.

static async Task PostAsJsonAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.PostAsJsonAsync(
        "todos", 
        new Todo(UserId: 9, Id: 99, Title: "Show extensions", Completed: false));

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var todo = await response.Content.ReadFromJsonAsync<Todo>();
    Console.WriteLine($"{todo}\n");

    // Expected output:
    //   POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
    //   Todo { UserId = 9, Id = 201, Title = Show extensions, Completed = False }
}

El código anterior:

  • Serializa la instancia Todo como JSON y realiza una solicitud POST a "https://jsonplaceholder.typicode.com/todos".
  • Garantiza que la respuesta es correcta y escribe los detalles de la solicitud en la consola.
  • Deserializa el cuerpo de la respuesta en una instancia Todo y escribe Todo en la consola.

HTTP Put

El método de solicitud PUT reemplaza un recurso existente o crea uno mediante la carga del cuerpo de la solicitud. Para realizar una solicitud HTTP PUT, con un HttpClient y un URI, use el método HttpClient.PutAsync:

static async Task PutAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new 
        {
            userId = 1,
            id = 1,
            title = "foo bar",
            completed = false
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PutAsync(
        "todos/1",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   PUT https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 1,
    //     "title": "foo bar",
    //     "completed": false
    //   }
}

El código anterior:

  • Prepara una instancia StringContent con el cuerpo JSON de la solicitud (tipo MIME de "application/json").
  • Realiza una solicitud PUT a "https://jsonplaceholder.typicode.com/todos/1".
  • Garantiza que la respuesta es correcta y escribe los detalles de la solicitud y el cuerpo de la respuesta JSON en la consola.

HTTP Put como JSON

Para serializar automáticamente los argumentos de solicitud PUT y deserializar las respuestas en objetos C# fuertemente tipados, use el método de extensión PutAsJsonAsync que forma parte del paquete NuGet System.Net.Http.Json.

static async Task PutAsJsonAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.PutAsJsonAsync(
        "todos/5",
        new Todo(Title: "partially update todo", Completed: true));

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var todo = await response.Content.ReadFromJsonAsync<Todo>();
    Console.WriteLine($"{todo}\n");

    // Expected output:
    //   PUT https://jsonplaceholder.typicode.com/todos/5 HTTP/1.1
    //   Todo { UserId = , Id = 5, Title = partially update todo, Completed = True }
}

El código anterior:

  • Serializa la instancia Todo como JSON y realiza una solicitud PUT a "https://jsonplaceholder.typicode.com/todos/5".
  • Garantiza que la respuesta es correcta y escribe los detalles de la solicitud en la consola.
  • Deserializa el cuerpo de la respuesta en una instancia Todo y escribe Todo en la consola.

HTTP Patch

La solicitud PATCH es una actualización parcial de un recurso existente. No crea ningún recurso y no está pensado para reemplazar un recurso existente. En su lugar, actualiza un recurso solo parcialmente. Para realizar una solicitud HTTP PATCH, con un HttpClient y un URI, use el método HttpClient.PatchAsync:

static async Task PatchAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new
        {
            completed = true
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PatchAsync(
        "todos/1",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output
    //   PATCH https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 1,
    //     "title": "delectus aut autem",
    //     "completed": true
    //   }
}

El código anterior:

  • Prepara una instancia StringContent con el cuerpo JSON de la solicitud (tipo MIME de "application/json").
  • Realiza una solicitud PATCH a "https://jsonplaceholder.typicode.com/todos/1".
  • Garantiza que la respuesta es correcta y escribe los detalles de la solicitud y el cuerpo de la respuesta JSON en la consola.

No existen métodos de extensión para las solicitudes PATCH en el paquete NuGet System.Net.Http.Json.

HTTP Delete

Una solicitud DELETE elimina un recurso existente. Una solicitud DELETE es idempotente pero no segura, lo que significa que varias solicitudes DELETE a los mismos recursos generan el mismo resultado, pero la solicitud afecta al estado del recurso. Para realizar una solicitud HTTP DELETE, con un HttpClient y un URI, use el método HttpClient.DeleteAsync:

static async Task DeleteAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.DeleteAsync("todos/1");
    
    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output
    //   DELETE https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {}
}

El código anterior:

  • Realiza una solicitud DELETE a "https://jsonplaceholder.typicode.com/todos/1".
  • Garantiza que la respuesta es correcta y escribe los detalles de la solicitud en la consola.

Sugerencia

La respuesta a una solicitud DELETE (al igual que una solicitud PUT) puede incluir o no un cuerpo.

HTTP Head

La solicitud HEAD es similar a una solicitud GET. En lugar de devolver el recurso, solo devuelve los encabezados asociados al recurso. Una respuesta a la solicitud HEAD no devuelve un cuerpo. Para realizar una solicitud HTTP HEAD, con un HttpClient y un URI, use el método HttpClient.SendAsync con HttpMethod establecido en HttpMethod.Head:

static async Task HeadAsync(HttpClient httpClient)
{
    using HttpRequestMessage request = new(
        HttpMethod.Head, 
        "https://www.example.com");

    using HttpResponseMessage response = await httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    foreach (var header in response.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    Console.WriteLine();

    // Expected output:
    //   HEAD https://www.example.com/ HTTP/1.1
    //   Accept-Ranges: bytes
    //   Age: 550374
    //   Cache-Control: max-age=604800
    //   Date: Wed, 10 Aug 2022 17:24:55 GMT
    //   ETag: "3147526947"
    //   Server: ECS, (cha / 80E2)
    //   X-Cache: HIT
}

El código anterior:

  • Realiza una solicitud HEAD a "https://www.example.com/".
  • Garantiza que la respuesta es correcta y escribe los detalles de la solicitud en la consola.
  • Recorre en iteración todos los encabezados de respuesta, escribiendo cada uno en la consola.

HTTP Options

La solicitud OPTIONS se usa para identificar qué métodos HTTP admite un servidor o punto de conexión. Para realizar una solicitud HTTP OPTIONS, con un HttpClient y un URI, use el método HttpClient.SendAsync con HttpMethod establecido en HttpMethod.Options:

static async Task OptionsAsync(HttpClient httpClient)
{
    using HttpRequestMessage request = new(
        HttpMethod.Options, 
        "https://www.example.com");

    using HttpResponseMessage response = await httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    foreach (var header in response.Content.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    Console.WriteLine();

    // Expected output
    //   OPTIONS https://www.example.com/ HTTP/1.1
    //   Allow: OPTIONS, GET, HEAD, POST
    //   Content-Type: text/html; charset=utf-8
    //   Expires: Wed, 17 Aug 2022 17:28:42 GMT
    //   Content-Length: 0
}

El código anterior:

  • Envía una solicitud HTTP OPTIONS a "https://www.example.com/".
  • Garantiza que la respuesta es correcta y escribe los detalles de la solicitud en la consola.
  • Recorre en iteración todos los encabezados de contenido de la respuesta, escribiendo cada uno en la consola.

HTTP Trace

La solicitud TRACE puede ser útil para la depuración, ya que proporciona un bucle invertido en el nivel de aplicación del mensaje de solicitud. Para realizar una solicitud HTTP TRACE, cree un objeto HttpRequestMessage mediante HttpMethod.Trace:

using HttpRequestMessage request = new(
    HttpMethod.Trace, 
    "{ValidRequestUri}");

Precaución

No todos los servidores HTTP admiten el método HTTP TRACE. Puede exponer una vulnerabilidad de seguridad si se usa de forma imprudente. Para obtener más información, vea Open Web Application Security Project (OWASP): seguimiento entre sitios.

Control de una respuesta HTTP

Siempre que controle una respuesta HTTP, interactúe con el tipo HttpResponseMessage. Se usan varios miembros al evaluar la validez de una respuesta. El código de estado HTTP está disponible a través de la propiedad HttpResponseMessage.StatusCode. Imagine que ha enviado una solicitud con una instancia de cliente:

using HttpResponseMessage response = await httpClient.SendAsync(request);

Para asegurarse de que response es OK (código de estado HTTP 200), puede evaluarlo como se muestra en el ejemplo siguiente:

if (response is { StatusCode: HttpStatusCode.OK })
{
    // Omitted for brevity...
}

Hay otros códigos de estado HTTP que representan una respuesta correcta, como CREATED (código de estado HTTP 201), ACCEPTED (código de estado HTTP 202), NO CONTENT (código de estado HTTP 204) y RESET CONTENT (código de estado HTTP 205). También puede usar la propiedad HttpResponseMessage.IsSuccessStatusCode para evaluar estos códigos, lo que garantiza que el código de estado de respuesta esté dentro del intervalo 200-299:

if (response.IsSuccessStatusCode)
{
    // Omitted for brevity...
}

Si necesita que el marco inicie HttpRequestException, puede llamar al método HttpResponseMessage.EnsureSuccessStatusCode():

response.EnsureSuccessStatusCode();

Este código produce HttpRequestException si el código de estado de respuesta no está dentro del intervalo de 200 a 299.

Respuestas de contenido válidas HTTP

Con una respuesta válida, puede acceder al cuerpo de la respuesta mediante la propiedad Content. El cuerpo está disponible como una instancia HttpContent, que puede usar para acceder al cuerpo como una secuencia, matriz de bytes o cadena:

await using Stream responseStream =
    await response.Content.ReadAsStreamAsync();

En el código anterior, se puede usar responseStream para leer el cuerpo de la respuesta.

byte[] responseByteArray = await response.Content.ReadAsByteArrayAsync();

En el código anterior, se puede usar responseByteArray para leer el cuerpo de la respuesta.

string responseString = await response.Content.ReadAsStringAsync();

En el código anterior, se puede usar responseString para leer el cuerpo de la respuesta.

Por último, cuando sepa que un punto de conexión HTTP devuelve JSON, puede deserializar el cuerpo de la respuesta en cualquier objeto de C# válido mediante el paquete NuGet System.Net.Http.Json:

T? result = await response.Content.ReadFromJsonAsync<T>();

En el código anterior, result es el cuerpo de la respuesta deserializado como el tipo T.

Control de errores HTTP

Cuando se produce un error en una solicitud HTTP, se inicia una excepción HttpRequestException. Es posible que la detección de esa excepción por sí sola no sea suficiente, ya que hay otras posibles excepciones iniciadas que debe tener en cuenta. Por ejemplo, el código de llamada puede haber usado un token de cancelación que se canceló antes de que se completara la solicitud. En este escenario, detectaría TaskCanceledException:

using var cts = new CancellationTokenSource();
try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/sleepFor?seconds=100", cts.Token);
}
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
{
    // When the token has been canceled, it is not a timeout.
    Console.WriteLine($"Canceled: {ex.Message}");
}

Del mismo modo, al realizar una solicitud HTTP, si el servidor no responde antes de que se supere HttpClient.Timeout, se inicia la misma excepción. Sin embargo, en este escenario, puede distinguir si ha habido tiempo de espera mediante la evaluación de Exception.InnerException al detectar TaskCanceledException:

try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/sleepFor?seconds=100");
}
catch (OperationCanceledException ex) when (ex.InnerException is TimeoutException tex)
{
    Console.WriteLine($"Timed out: {ex.Message}, {tex.Message}");
}

En el código anterior, cuando la excepción interna es TimeoutException, hay tiempo de espera, y el token de cancelación no canceló la solicitud.

Para evaluar el código de estado HTTP al detectar HttpRequestException, puede evaluar la propiedad HttpRequestException.StatusCode:

try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/doesNotExist");

    response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    // Handle 404
    Console.WriteLine($"Not found: {ex.Message}");
}

En el código anterior, se llama al método EnsureSuccessStatusCode() para producir una excepción si la respuesta no es correcta. A continuación, la propiedad HttpRequestException.StatusCode se evalúa para determinar si la respuesta era 404 (código de estado HTTP 404). Hay varios métodos auxiliares en HttpClient que llaman implícitamente a EnsureSuccessStatusCode en su nombre; tenga en cuenta las siguientes API:

Sugerencia

Todos los métodos HttpClient usados para realizar solicitudes HTTP que no devuelven HttpResponseMessage llaman de forma implícita a EnsureSuccessStatusCode en su nombre.

Al llamar a estos métodos, puede controlar HttpRequestException y evaluar la propiedad HttpRequestException.StatusCode para determinar el código de estado HTTP de la respuesta:

try
{
    // These extension methods will throw HttpRequestException
    // with StatusCode set when the HTTP request status code isn't 2xx:
    //
    //   GetByteArrayAsync
    //   GetStreamAsync
    //   GetStringAsync

    using var stream = await httpClient.GetStreamAsync(
        "https://localhost:5001/doesNotExists");
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    // Handle 404
    Console.WriteLine($"Not found: {ex.Message}");
}

Puede haber escenarios en los que es necesario iniciar la excepción HttpRequestException en el código. El constructor HttpRequestException() es público y puede usarlo para iniciar una excepción con un mensaje personalizado:

try
{
    using var response = await httpClient.GetAsync(
        "https://localhost:5001/doesNotExists");

    // Throw for anything higher than 400.
    if (response is { StatusCode: >= HttpStatusCode.BadRequest })
    {
        throw new HttpRequestException(
            "Something went wrong", inner: null, response.StatusCode);
    }
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    Console.WriteLine($"Not found: {ex.Message}");
}

Proxy HTTP

Un proxy HTTP se puede configurar de dos maneras. Se especifica un valor predeterminado en la propiedad HttpClient.DefaultProxy. Como alternativa, puede especificar un proxy en la propiedad HttpClientHandler.Proxy.

Proxy predeterminado global

HttpClient.DefaultProxy es una propiedad estática que determina el proxy predeterminado que todas las instancias HttpClient usan si no se establece ningún proxy explícitamente en el objeto HttpClientHandler pasado a través de su constructor.

La instancia predeterminada devuelta por esta propiedad se inicializa después de un conjunto diferente de reglas en función de la plataforma:

  • Para Windows: lee la configuración de proxy de las variables de entorno o, si no se definen, de la configuración del proxy del usuario.
  • Para macOS: lee la configuración de proxy de las variables de entorno o, si no se definen, de la configuración del proxy del sistema.
  • Para Linux: lee la configuración de proxy de las variables de entorno o, si no se definen, esta propiedad inicializa una instancia no configurada que omite todas las direcciones.

Las variables de entorno que se usan para la inicialización de DefaultProxy en plataformas basadas en Windows y Unix son las siguientes:

  • HTTP_PROXY: el servidor proxy que se usa en solicitudes HTTP.
  • HTTPS_PROXY: el servidor proxy que se usa en solicitudes HTTP.
  • ALL_PROXY: el servidor proxy utilizado en solicitudes HTTP o HTTPS en caso de que HTTP_PROXY o HTTPS_PROXY no se definan.
  • NO_PROXY: una lista separada por comas de nombres de host que se deben excluir del proxy. No se admiten asteriscos para caracteres comodín; use un punto inicial en caso de que desee que coincida con un subdominio. Ejemplos: NO_PROXY=.example.com (con punto inicial) coincidirá con www.example.com, pero no coincidirá con example.com. NO_PROXY=example.com (sin punto inicial) no coincidirá con www.example.com. Este comportamiento se podría revisar en el futuro para que coincida mejor con otros ecosistemas.

En sistemas en los que las variables de entorno distinguen entre mayúsculas y minúsculas, los nombres de las variables pueden estar todas en minúsculas o en mayúsculas. Los nombres en minúsculas se comprueban primero.

El servidor proxy puede ser un nombre de host o una dirección IP, seguido opcionalmente de dos puntos y un número de puerto, o puede ser una dirección URL http, que puede incluir un nombre de usuario y una contraseña para la autenticación de proxy. La dirección URL debe comenzar con http, no con https, y no puede incluir ningún texto después del nombre de host, la dirección IP o el puerto.

Proxy por cliente

La propiedad HttpClientHandler.Proxy identifica el objeto WebProxy que se va a usar para procesar solicitudes a recursos de Internet. Para especificar que no se debe usar ningún proxy, establezca la propiedad Proxy en la instancia de proxy devuelta por el método GlobalProxySelection.GetEmptyWebProxy().

El equipo local o el archivo de configuración de la aplicación pueden especificar que se use un proxy predeterminado. Si se especifica la propiedad Proxy, la configuración del proxy de la propiedad Proxy reemplaza al archivo de configuración del equipo local o la aplicación, y el controlador usa la configuración de proxy especificada. Si no se especifica ningún proxy en un archivo de configuración y tampoco se especifica la propiedad Proxy, el controlador usa la configuración de proxy heredada del equipo local. Si no hay ninguna configuración de proxy, la solicitud se envía directamente al servidor.

La clase HttpClientHandler analiza una lista de omisión de proxy con caracteres comodín heredados de la configuración del equipo local. Por ejemplo, la clase HttpClientHandler analiza una lista de omisión de "nt*" de exploradores como una expresión regular de "nt.*". Por lo tanto, una dirección URL de http://nt.com omitiría el proxy mediante la clase HttpClientHandler.

La clase HttpClientHandler admite la omisión del proxy local. La clase considera que un destino es local si se cumple alguna de las condiciones siguientes:

  1. El destino contiene un nombre plano (sin puntos en la dirección URL).
  2. El destino contiene una dirección de bucle invertido (Loopback o IPv6Loopback) o el destino contiene un IPAddress asignado al equipo local.
  3. El sufijo de dominio del destino coincide con el sufijo de dominio del equipo local (DomainName).

Para obtener más información acerca de cómo configurar un proxy, vea:

Vea también