Partilhar via


Fazer solicitações HTTP com a classe HttpClient

Neste artigo, você aprenderá a fazer solicitações HTTP e lidar com respostas com a classe HttpClient.

Importante

Todas as solicitações HTTP de exemplo neste artigo visam uma das seguintes URLs:

Os pontos de extremidade HTTP geralmente retornam dados JSON (JavaScript Object Notation), mas nem sempre. Por conveniência, o pacote opcional System.Net.Http.Json NuGet fornece vários métodos de extensão para objetos HttpClient e HttpContent que executam serialização e desserialização automáticas usando o pacote 📦 System.Text.Json NuGet. Os exemplos neste artigo chamam a atenção para locais onde essas extensões estão disponíveis.

Gorjeta

Todo o código-fonte mencionado neste artigo está disponível no repositório GitHub: .NET Docs.

Criar um objeto HttpClient

A maioria dos exemplos neste artigo reutiliza a mesma instância HttpClient, para que você possa configurar a instância uma vez e usá-la para os exemplos restantes. Para criar um objeto HttpClient, use o construtor de classe HttpClient. Para obter mais informações, consulte Diretrizes 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"),
};

O código conclui as seguintes tarefas:

  • Instancie uma nova instância HttpClient como uma variável static. De acordo com as diretrizes , a abordagem recomendada é reutilizar instâncias HttpClient durante o ciclo de vida do aplicativo.
  • Defina a propriedade HttpClient.BaseAddress como "https://jsonplaceholder.typicode.com".

Essa instância HttpClient usa o endereço base para fazer solicitações subsequentes. Para aplicar outras configurações, considere as seguintes APIs:

Gorjeta

Como alternativa, você pode criar instâncias de HttpClient usando uma abordagem de padrão de fábrica que permite configurar qualquer número de clientes e consumi-los como serviços de injeção de dependência. Para obter mais informações, consulte Fábrica de cliente HTTP com .NET.

Fazer uma solicitação HTTP

Para fazer uma solicitação HTTP, chame qualquer um dos seguintes métodos de API:

Método HTTP 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

Uma solicitação de USER SPECIFIED indica que o método SendAsync aceita qualquer objeto HttpMethod válido.

Aviso

Fazer solicitações HTTP é considerado trabalho vinculado a E/S de rede. Existe um método HttpClient.Send síncrono, mas a recomendação é usar as APIs assíncronas, a menos que você tenha um bom motivo para não fazê-lo.

Nota

Ao direcionar dispositivos Android, como no desenvolvimento do .NET MAUI, deve-se adicionar a definição de android:usesCleartextTraffic="true" à seção <application></application> no arquivo AndroidManifest.xml. Essa configuração permite o tráfego de texto não criptografado, como solicitações HTTP, que de outra forma é desativado por padrão devido às políticas de segurança do Android. Considere o seguinte exemplo de configurações XML:

<?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 obter mais informações, consulte Habilitar tráfego de rede de texto não criptografado para o domínio localhost.

Compreender o conteúdo HTTP

O HttpContent tipo é usado para representar um corpo de entidade HTTP e cabeçalhos de conteúdo correspondentes. Para métodos HTTP (ou métodos de solicitação) que exigem um corpo (POST, PUT, PATCH), use a classe HttpContent para especificar o corpo da solicitação. A maioria dos exemplos mostra como preparar a StringContent subclasse com uma carga JSON útil, mas existem outras subclasses para diferentes tipos de conteúdo (MIME).

A classe HttpContent também é usada para representar o corpo de resposta da classe HttpResponseMessage, que é acessível na propriedade HttpResponseMessage.Content.

Usar uma solicitação HTTP GET

Um pedido de GET não deve enviar um corpo. Essa solicitação é usada (como o nome do método indica) para recuperar (ou obter) dados de um recurso. Para fazer uma solicitação de GET HTTP dada uma instância HttpClient e um objeto Uri, use o 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
    //   }
}

O código conclui as seguintes tarefas:

  • Faça uma solicitação de GET ao endpoint "https://jsonplaceholder.typicode.com/todos/3".
  • Certifique-se de que a resposta é bem-sucedida.
  • Escreva os detalhes da solicitação no console.
  • Leia o corpo da resposta como uma cadeia de caracteres.
  • Escreva o corpo da resposta JSON no console.

O método WriteRequestToConsole é uma extensão personalizada que não faz parte da estrutura. Se você estiver curioso sobre a implementação, considere o seguinte código 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}");        
    }
}

Essa funcionalidade é usada para gravar os detalhes da solicitação no console da seguinte forma:

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

Por exemplo, a solicitação de GET ao endpoint "https://jsonplaceholder.typicode.com/todos/3" produz a seguinte mensagem:

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

Criar a solicitação HTTP GET a partir de JSON

O ponto de extremidade https://jsonplaceholder.typicode.com/todos retorna uma matriz JSON de objetos Todo. Sua estrutura JSON é semelhante à seguinte forma:

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

O objeto C# Todo é definido da seguinte forma:

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

É um record class tipo, com opcional Id, Title, Completed, e UserId propriedades. Para obter mais informações sobre o record tipo, consulte Introdução aos tipos de registro em C#. Para desserializar automaticamente pedidos GET num objeto C# fortemente tipado, utilize o método de extensão GetFromJsonAsync que faz parte do pacote 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 }
}

O código conclui as seguintes tarefas:

  • Faça uma solicitação de GET para "https://jsonplaceholder.typicode.com/todos?userId=1&completed=false".

    A cadeia de caracteres de consulta representa os critérios de filtragem para a solicitação. Quando o comando é bem-sucedido, a resposta é automaticamente desserializada em um objeto List<Todo>.

  • Escreva os detalhes da solicitação no console, juntamente com cada objeto Todo.

Usar uma solicitação HTTP POST

Uma POST solicitação envia dados ao servidor para processamento. O Content-Type cabeçalho da solicitação indica qual tipo MIME o corpo está a enviar. Para fazer uma solicitação de POST HTTP dada uma instância HttpClient e um objeto Uri, use o 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
    //   }
}

O código conclui as seguintes tarefas:

  • Prepare uma instância StringContent com o corpo JSON da solicitação (tipo MIME de "application/json").
  • Faça uma solicitação de POST para o ponto de extremidade "https://jsonplaceholder.typicode.com/todos".
  • Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
  • Escreva o corpo da resposta como uma cadeia de caracteres no console.

Criar a solicitação HTTP POST como JSON

Para serializar automaticamente POST argumentos de pedido e desserializar respostas em objetos fortemente tipados em C#, use o método de extensão PostAsJsonAsync que faz parte do conjunto de ferramentas 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 }
}

O código conclui as seguintes tarefas:

  • Serialize a instância Todo como JSON e faça uma solicitação POST para o ponto de extremidade "https://jsonplaceholder.typicode.com/todos".
  • Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
  • Desserialize o corpo da resposta em uma instância Todo e grave o objeto Todo no console.

Usar uma solicitação HTTP PUT

O método de solicitação PUT substitui um recurso existente ou cria um novo usando a carga útil do corpo da solicitação. Para fazer uma solicitação de PUT HTTP dada uma instância HttpClient e um objeto Uri, use o 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
    //   }
}

O código conclui as seguintes tarefas:

  • Prepare uma instância StringContent com o corpo JSON da solicitação (tipo MIME de "application/json").
  • Faça um pedido PUT para o endpoint "https://jsonplaceholder.typicode.com/todos/1".
  • Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação com o corpo da resposta JSON no console.

Criar a solicitação HTTP PUT como JSON

Para serializar PUT automaticamente argumentos de solicitação e desserializar respostas em objetos C# fortemente tipados, use o PutAsJsonAsync método de extensão que faz parte do pacote 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 }
}

O código conclui as seguintes tarefas:

  • Serialize a instância Todo como JSON e faça uma solicitação PUT para o ponto de extremidade "https://jsonplaceholder.typicode.com/todos/5".
  • Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
  • Desserialize o corpo da resposta em uma instância Todo e grave os objetos Todo no console.

Utilizar uma solicitação HTTP PATCH

A PATCH solicitação é uma atualização parcial de um recurso existente. Esta solicitação não cria um novo recurso e não se destina a substituir um recurso existente. Em vez disso, esse método atualiza apenas parcialmente um recurso. Para fazer uma solicitação de PATCH HTTP dada uma instância HttpClient e um objeto Uri, use o 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
    //   }
}

O código conclui as seguintes tarefas:

  • Prepare uma instância StringContent com o corpo JSON da solicitação (tipo MIME de "application/json").
  • Faça uma solicitação de PATCH ao ponto final "https://jsonplaceholder.typicode.com/todos/1".
  • Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação com o corpo da resposta JSON no console.

Não existem métodos de extensão para PATCH solicitações no System.Net.Http.Json pacote NuGet.

Utilize uma solicitação HTTP DELETE

Uma solicitação de DELETE remove um recurso existente e a solicitação é idempotente, mas não segura. Várias solicitações de DELETE para os mesmos recursos produzem o mesmo resultado, mas a solicitação afeta o estado do recurso. Para fazer uma solicitação de DELETE HTTP dada uma instância HttpClient e um objeto Uri, use o 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
    //   {}
}

O código conclui as seguintes tarefas:

  • Faça uma solicitação de DELETE ao endpoint "https://jsonplaceholder.typicode.com/todos/1".
  • Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.

Gorjeta

A resposta a uma solicitação de DELETE (assim como uma solicitação de PUT) pode ou não incluir um corpo.

Explorar a solicitação HEAD do HTTP

O HEAD pedido é semelhante a um GET pedido. Em vez de retornar o recurso, essa solicitação retorna apenas os cabeçalhos associados ao recurso. Uma resposta ao HEAD pedido não devolve um corpo. Para fazer uma solicitação de HEAD HTTP dada uma instância HttpClient e um objeto Uri, use o método HttpClient.SendAsync com o tipo HttpMethod definido como 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
}

O código conclui as seguintes tarefas:

  • Faça um pedido de HEAD para o endpoint "https://www.example.com/".
  • Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
  • Percorra todos os cabeçalhos de resposta e escreva cada cabeçalho na consola.

Explore a solicitação HTTP OPTIONS

A OPTIONS solicitação é usada para identificar quais métodos HTTP um servidor ou ponto de extremidade suporta. Para fazer uma solicitação de OPTIONS HTTP dada uma instância HttpClient e um objeto Uri, use o método HttpClient.SendAsync com o tipo HttpMethod definido como 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
}

O código conclui as seguintes tarefas:

  • Envie uma solicitação HTTP OPTIONS para o endereço "https://www.example.com/".
  • Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
  • Itere todos os cabeçalhos de conteúdo de resposta e escreva cada cabeçalho no console.

Explore o pedido HTTP TRACE

A TRACE solicitação pode ser útil para depuração, pois fornece loopback ao nível da aplicação da mensagem de solicitação. Para fazer uma solicitação de TRACE HTTP, crie um HttpRequestMessage usando o tipo HttpMethod.Trace:

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

Atenção

Nem todos os servidores HTTP suportam o método HTTP TRACE. Esse método pode expor uma vulnerabilidade de segurança se usado de forma imprudente. Para obter mais informações, consulte Open Web Application Security Project (OWASP): Cross Site Tracing.

Manipular uma resposta HTTP

Ao manipular uma resposta HTTP, você interage com o tipo HttpResponseMessage. Vários membros são usados para avaliar a validade de uma resposta. O código de status HTTP está disponível na propriedade HttpResponseMessage.StatusCode.

Suponha que uma pessoa envie uma solicitação usando uma instância do cliente:

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

Para garantir que o response seja OK (código de status HTTP 200), você pode avaliar o valor conforme mostrado no exemplo a seguir:

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

Existem outros códigos de status HTTP que representam uma resposta bem-sucedida, como CREATED (código de status HTTP 201), ACCEPTED (código de status HTTP 202), NO CONTENT (código de status HTTP 204) e RESET CONTENT (código de status HTTP 205). Você também pode usar a HttpResponseMessage.IsSuccessStatusCode propriedade para avaliar esses códigos, o que garante que o código de status da resposta esteja dentro do intervalo 200-299:

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

Se você precisar que a estrutura lance o erro HttpRequestException, você pode chamar o método HttpResponseMessage.EnsureSuccessStatusCode():

response.EnsureSuccessStatusCode();

Esse código lança um erro de HttpRequestException se o código de status da resposta não estiver dentro do intervalo 200-299.

Explore respostas de conteúdo HTTP válidas

Com uma resposta válida, você pode acessar o corpo da resposta usando a propriedade Content. O corpo está disponível como uma instância HttpContent, que você pode usar para acessar o corpo como um fluxo, matriz de bytes ou cadeia de caracteres.

O código a seguir usa o objeto responseStream para ler o corpo da resposta:

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

Você pode usar objetos diferentes para ler o corpo da resposta. Use o objeto responseByteArray para ler o corpo da resposta:

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

Use o objeto responseString para ler o corpo da resposta:

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

Quando sabes que um endpoint HTTP retorna JSON, podes desserializar o corpo da resposta em qualquer objeto C# válido usando o pacote NuGet System.Net.Http.Json:

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

Neste código, o valor result é o corpo da resposta desserializado como o tipo T.

Usar tratamento de erros HTTP

Quando uma solicitação HTTP falha, o sistema lança o objeto HttpRequestException. Capturar a exceção por si só pode não ser suficiente. Há outras exceções potenciais que poderá querer considerar para tratamento. Por exemplo, o código de chamada pode usar um token de cancelamento que foi cancelado antes da conclusão da solicitação. Neste cenário, podes detetar o erro 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}");
}

Da mesma forma, quando você faz uma solicitação HTTP, se o servidor não responder antes que o valor HttpClient.Timeout seja excedido, a mesma exceção será lançada. Nesse cenário, você pode distinguir que o timeout ocorreu avaliando a propriedade Exception.InnerException ao detetar o erro 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}");
}

No código, quando a exceção interna é um tipo TimeoutException, o tempo limite ocorreu e o token de cancelamento não cancela a solicitação.

Para avaliar o código de status HTTP quando você captura o objeto HttpRequestException, você pode avaliar a propriedade 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}");
}

No código, o método EnsureSuccessStatusCode() é chamado para lançar uma exceção se a resposta não for bem-sucedida. A propriedade HttpRequestException.StatusCode é então avaliada para determinar se a resposta foi um código de status HTTP 404 (404). Há vários métodos auxiliares no objeto HttpClient que implicitamente chamam o método EnsureSuccessStatusCode em seu nome.

Para o tratamento de erros HTTP, considere as seguintes APIs:

Gorjeta

Todos os métodos HttpClient usados para fazer solicitações HTTP que não retornam um tipo de HttpResponseMessage chamam implicitamente o método EnsureSuccessStatusCode em seu nome.

Quando você chama esses métodos, você pode manipular o objeto HttpRequestException e avaliar a propriedade HttpRequestException.StatusCode para determinar o código de status HTTP da resposta:

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

Pode haver cenários em que você precise lançar o objeto HttpRequestException em seu código. O construtor HttpRequestException() é público e você pode usá-lo para lançar uma exceção com uma mensagem personalizada:

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

Configurar um proxy HTTP

Um proxy HTTP pode ser configurado de duas maneiras. Na propriedade HttpClient.DefaultProxy é especificado um padrão. Como alternativa, você pode especificar um proxy na HttpClientHandler.Proxy propriedade.

Usar um proxy padrão global

A propriedade HttpClient.DefaultProxy é uma propriedade estática que determina o proxy padrão que todas as instâncias HttpClient usam, se nenhum proxy for definido explicitamente no objeto HttpClientHandler passado por seu construtor.

A instância padrão retornada por essa propriedade é inicializada de acordo com um conjunto diferente de regras, dependendo da sua plataforma:

  • Windows: Ler a configuração de proxy a partir das variáveis de ambiente ou, se estas não estiverem definidas, ler das configurações de proxy do utilizador.
  • macOS: Leia a configuração de proxy a partir de variáveis de ambiente ou, se as variáveis não estiverem definidas, leia as configurações de proxy do sistema.
  • Linux: Leia a configuração de proxy a partir de variáveis de ambiente ou, se as variáveis não estiverem definidas, inicialize uma instância não configurada para ignorar todos os endereços.

A inicialização da propriedade DefaultProxy em plataformas baseadas em Windows e Unix usa as seguintes variáveis de ambiente:

  • HTTP_PROXY: O servidor proxy usado em solicitações HTTP.
  • HTTPS_PROXY: O servidor proxy usado em solicitações HTTPS.
  • ALL_PROXY: O servidor proxy usado em solicitações HTTP e/ou HTTPS quando as variáveis HTTP_PROXY e/ou HTTPS_PROXY não estão definidas.
  • NO_PROXY: Uma lista, separada por vírgulas, de nomes de host a excluir do processo de proxy. Asteriscos não são suportados para caracteres universais. Use um ponto inicial (.) quando quiser corresponder a um subdomínio. Exemplos: NO_PROXY=.example.com (com ponto à frente) corresponde a www.example.com, mas não corresponde a example.com. NO_PROXY=example.com (sem ponto inicial) não corresponde a www.example.com. Este comportamento pode ser revisitado no futuro para corresponder melhor a outros ecossistemas.

Em sistemas em que as variáveis de ambiente são sensíveis a maiúsculas e minúsculas, os nomes das variáveis podem ser todos minúsculos ou todos maiúsculos. Os nomes minúsculos são verificados primeiro.

O servidor proxy pode ser um nome de host ou endereço IP, opcionalmente seguido por dois pontos e número de porta, ou pode ser uma URL http, opcionalmente incluindo um nome de usuário e senha para autenticação de proxy. A URL deve começar com http, não httpse não pode incluir nenhum texto após o nome do host, IP ou porta.

Configurar o proxy para cada cliente

A propriedade HttpClientHandler.Proxy identifica o objeto WebProxy a ser usado para processar solicitações para recursos da Internet. Para especificar que nenhum proxy deve ser usado, defina a Proxy propriedade como a instância de proxy retornada pelo método GlobalProxySelection.GetEmptyWebProxy().

O computador local ou o arquivo de configuração do aplicativo pode especificar que um proxy padrão seja usado. Se a propriedade Proxy for especificada, as configurações de proxy da propriedade Proxy substituirão o computador local ou o arquivo de configuração do aplicativo e o manipulador usará as configurações de proxy especificadas. Se nenhum proxy for especificado em um arquivo de configuração e a propriedade Proxy não for especificada, o manipulador usará as configurações de proxy herdadas do computador local. Se não houver configurações de proxy, a solicitação será enviada diretamente para o servidor.

A classe HttpClientHandler analisa uma lista de exceções de proxy com caracteres curinga herdados das configurações do computador local. Por exemplo, a HttpClientHandler classe analisa uma lista de bypass de "nt*" navegadores como uma expressão regular de "nt.*". Portanto, uma URL de http://nt.com ignora o proxy usando a classe HttpClientHandler.

A classe HttpClientHandler suporta bypass local de proxy. A classe considera um destino como local se qualquer uma das seguintes condições for atendida:

  • O destino contém um nome simples (sem pontos (.) no URL).
  • O destino contém um endereço de loopback (Loopback ou IPv6Loopback) ou o destino contém uma propriedade IPAddress atribuída ao computador local.
  • O sufixo de domínio do destino corresponde ao sufixo de domínio do computador local, conforme definido na propriedade DomainName.

Para obter mais informações sobre como configurar um proxy, consulte as seguintes APIs:

Próximos passos