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 têm como destino uma das seguintes URLs:
- https://jsonplaceholder.typicode.com: um site que fornece uma plataforma de API falsa gratuita para teste e protótipo.
- https://www.example.com: um domínio disponível para uso em exemplos ilustrativos em documentos.
Os pontos de extremidade HTTP normalmente retornam dados JSON (JavaScript Object Notation), mas nem sempre. Por conveniência, o pacote NuGet System.Net.Http.Json opcional 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 em que essas extensões estão disponíveis.
Dica
Todo o código-fonte referenciado neste artigo está disponível no GitHub: .NET Docs repositório.
Criar um objeto HttpClient
A maioria dos exemplos neste artigo reutiliza a mesma instância de 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, confira 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 de
HttpClient
como uma variávelstatic
. De acordo com as diretrizes de, a abordagem recomendada é reutilizar as instânciasHttpClient
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:
- Definir a propriedade HttpClient.DefaultRequestHeaders.
- Aplique uma propriedade HttpClient.Timeout não padrão.
- Especifique a propriedade HttpClient.DefaultRequestVersion.
Dica
Como alternativa, você pode criar instâncias HttpClient
usando uma abordagem de padrão de fábrica que permite configurar qualquer número de clientes e consumi-las como serviços de injeção de dependência. Para obter mais informações, consulte fábrica de clientes HTTP com o .NET.
Fazer uma solicitação HTTP
Para fazer uma solicitação HTTP, você chama 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étodoSendAsync
aceita qualquer objeto de HttpMethod válido.
Aviso
Fazer solicitações HTTP é considerado um trabalho associado a E/S de rede. Existe um método de HttpClient.Send síncrono, mas a recomendação é usar as APIs assíncronas, a menos que você tenha um bom motivo para não fazer isso.
Observação
Ao direcionar o desenvolvimento para dispositivos Android (como no .NET MAUI), você deve adicionar a definição android:usesCleartextTraffic="true"
à seção <application></application>
no arquivo AndroidManifest.xml. Essa configuração habilita o tráfego de texto limpo, como solicitações HTTP, que de outra forma está desabilitada 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 o tráfego de rede de texto claro para o domínio localhost.
Entender o conteúdo HTTP
O tipo HttpContent é 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
), você usa a classe HttpContent para especificar o corpo da solicitação. A maioria dos exemplos mostra como preparar a subclasse StringContent com um conteúdo JSON, mas existem outras subclasses para diferentes tipos de conteúdo (MIME).
- ByteArrayContent: fornece conteúdo HTTP baseado em uma matriz de bytes.
- FormUrlEncodedContent: fornece conteúdo HTTP para tuplas de nome/valor codificadas usando o tipo MIME
"application/x-www-form-urlencoded"
. - JsonContent: fornece conteúdo HTTP baseado em JSON.
- MultipartContent: fornece uma coleção de objetos HttpContent que são serializados usando a especificação de tipo MIME
"multipart/*"
. - MultipartFormDataContent: fornece um contêiner para conteúdo codificado usando o tipo MIME
"multipart/form-data"
. - ReadOnlyMemoryContent: fornece conteúdo HTTP com base em um valor ReadOnlyMemory<T>.
- StreamContent: fornece conteúdo HTTP baseado em um fluxo.
- StringContent: fornece conteúdo HTTP baseado em uma cadeia de caracteres.
A classe HttpContent
também é usada para representar o corpo da resposta da classe HttpResponseMessage, que é acessível na propriedade HttpResponseMessage.Content.
Usar uma solicitação HTTP GET
Uma solicitação 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 http GET
dada uma instância de 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
GET
ao endpoint"https://jsonplaceholder.typicode.com/todos/3"
. - Verifique se a resposta foi bem-sucedida.
- Escreva os detalhes da solicitação no console.
- Leia o corpo da resposta como uma cadeia de caracteres.
- Grave 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 no seguinte formulário:
<HTTP Request Method> <Request URI> <HTTP/Version>
Por exemplo, a solicitação GET
para o endpoint "https://jsonplaceholder.typicode.com/todos/3"
gera a seguinte mensagem:
GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1
Criar a solicitação HTTP GET do 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 Todo
do C# é definido da seguinte maneira:
public record class Todo(
int? UserId = null,
int? Id = null,
string? Title = null,
bool? Completed = null);
É um tipo record class
, com propriedades opcionais Id
, Title
, Completed
e UserId
. Para obter mais informações sobre o tipo record
, confira Introdução aos tipos de registro em C#. Para desserializar automaticamente solicitações GET
em um objeto C# fortemente tipado, use 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
GET
para"https://jsonplaceholder.typicode.com/todos?userId=1&completed=false"
.A cadeia de caracteres de consulta representa os critérios de filtragem da solicitação. Quando o comando é bem-sucedido, a resposta é desserializada automaticamente 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 solicitação POST
envia dados ao servidor para processamento. O cabeçalho da solicitação Content-Type
significa qual tipo MIME o corpo está enviando. Para fazer uma solicitação http POST
dada uma instância de 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 a instância StringContent com o corpo JSON da solicitação (tipo MIME de
"application/json"
). - Faça uma solicitação
POST
para o endpoint"https://jsonplaceholder.typicode.com/todos"
. - Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
- Grave o corpo da resposta como uma cadeia de caracteres no console.
Criar a solicitação HTTP POST como JSON
Para serializar automaticamente argumentos de solicitação POST
e desserializar respostas em objetos C# fortemente tipados, use o método de extensão PostAsJsonAsync que faz parte do pacote 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 de
Todo
como JSON e faça uma solicitaçãoPOST
para o endpoint"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 objetoTodo
no console.
Usar uma solicitação HTTP PUT
O método de solicitação PUT
substitui um recurso existente ou cria um novo usando o conteúdo do corpo da solicitação. Para fazer uma solicitação http PUT
dada uma instância de 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 uma solicitação
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 automaticamente argumentos de solicitação PUT
e desserializar respostas em objetos C# fortemente tipados, use o método de extensão PutAsJsonAsync 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 de
Todo
como JSON e faça uma solicitaçãoPUT
para o endpoint"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 objetosTodo
no console.
Usar uma solicitação HTTP PATCH
A solicitação PATCH
é uma atualização parcial de um recurso existente. Essa 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 http PATCH
dada uma instância de 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
PATCH
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.
Não existem métodos de extensão para solicitações PATCH
no pacote NuGet System.Net.Http.Json
.
Usar uma solicitação HTTP DELETE
Uma solicitação DELETE
remove um recurso existente e a solicitação é idempotente, mas não é segura. Várias solicitações DELETE
para os mesmos recursos produzem o mesmo resultado, mas a solicitação afeta o estado do recurso. Para fazer uma solicitação http DELETE
dada uma instância de 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:
- Realize uma solicitação
DELETE
para o endpoint"https://jsonplaceholder.typicode.com/todos/1"
. - Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
Dica
A resposta a uma solicitação DELETE
(assim como uma solicitação PUT
) pode ou não incluir um corpo.
Explorar a solicitação HTTP HEAD
A solicitação HEAD
é semelhante a uma solicitação GET
. Em vez de retornar o recurso, essa solicitação retorna apenas os cabeçalhos associados ao recurso. Uma resposta à solicitação HEAD
não retorna um corpo. Para fazer uma solicitação http HEAD
dada uma instância de 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 uma solicitação
HEAD
ao endpoint"https://www.example.com/"
. - Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
- Itere em todos os cabeçalhos de resposta e grave cada cabeçalho no console.
Explorar a solicitação HTTP OPTIONS
A solicitação OPTIONS
é usada para identificar a quais métodos HTTP um servidor ou ponto de extremidade dá suporte. Para fazer uma solicitação http OPTIONS
dada uma instância de 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 ponto de extremidade"https://www.example.com/"
. - Verifique se a resposta foi bem-sucedida e escreva os detalhes da solicitação no console.
- Itere em todos os cabeçalhos de conteúdo de resposta e grave cada cabeçalho no console.
Explorar a solicitação HTTP TRACE
A solicitação TRACE
pode ser útil para a depuração, pois fornece retorno da mensagem de solicitação ao nível da aplicação. Para fazer uma solicitação http TRACE
, crie um HttpRequestMessage usando o tipo HttpMethod.Trace
:
using HttpRequestMessage request = new(
HttpMethod.Trace,
"{ValidRequestUri}");
Cuidado
Nem todos os servidores HTTP dão suporte ao método HTTP TRACE
. Esse método pode expor uma vulnerabilidade de segurança se usado de forma imprudente. Para obter mais informações, confira o texto sobre Rastreamento entre sites da OWASP (Open Web Application Security Project).
Lidar com uma resposta HTTP
Ao lidar com 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 você envie uma solicitação determinada a 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...
}
Há 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 propriedade HttpResponseMessage.IsSuccessStatusCode para avaliar esses códigos, o que garante que o código de status de resposta esteja dentro do intervalo de 200 a 299:
if (response.IsSuccessStatusCode)
{
// Omitted for brevity...
}
Se você precisar que a estrutura gere o erro HttpRequestException, você pode chamar o método HttpResponseMessage.EnsureSuccessStatusCode():
response.EnsureSuccessStatusCode();
Esse código gera um erro HttpRequestException
se o código de status de resposta não estiver dentro do intervalo 200-299.
Explorar respostas de conteúdo válidas para HTTP
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 diferentes objetos para ler o conteúdo 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 você sabe que um ponto de extremidade HTTP retorna JSON, você pode 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 o tratamento de erros HTTP
Quando uma solicitação HTTP falha, o sistema lança o objeto HttpRequestException. Capturar apenas a exceção pode não ser suficiente. Há outras possíveis exceções geradas que você talvez considere tratar. 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, você pode capturar 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 do HttpClient.Timeout seja excedido, a mesma exceção será gerada. Nesse cenário, você pode identificar que ocorreu um timeout avaliando a propriedade Exception.InnerException ao capturar 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 de TimeoutException, o tempo limite ocorreu e o token de cancelamento não cancela a solicitação.
Para avaliar o código de status HTTP ao capturar 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 gerar uma exceção se a resposta não for bem-sucedida. A propriedade HttpRequestException.StatusCode é avaliada em seguida para determinar se a resposta foi um 404
(código de status HTTP 404). Há vários métodos auxiliares no objeto HttpClient
que chamam implicitamente o método EnsureSuccessStatusCode
em seu nome.
Para o tratamento de erros HTTP, considere as seguintes APIs:
- método HttpClient.GetByteArrayAsync
- método HttpClient.GetStreamAsync
- método HttpClient.GetStringAsync
Dica
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.
Ao chamar 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ê precisa lançar o objeto HttpRequestException em seu código. O construtor HttpRequestException() é público e você pode usá-lo para gerar 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. Um padrão é especificado na propriedade HttpClient.DefaultProxy. Como alternativa, você pode especificar um proxy na propriedade HttpClientHandler.Proxy.
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 inicializa de acordo com um conjunto diferente de regras, dependendo da plataforma:
- Windows: Leia a configuração de proxy das variáveis de ambiente ou, se as variáveis não estiverem definidas, leia as configurações de proxy do usuário.
- macOS: leia a configuração de proxy com base em variáveis de ambiente ou, se as variáveis não estiverem definidas, leia com base nas configurações de proxy do sistema.
- Linux: ler a configuração de proxy das variáveis de ambiente ou, se as variáveis não estiverem definidas, inicializar 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áveisHTTP_PROXY
e/ouHTTPS_PROXY
não são definidas.-
NO_PROXY
: uma lista separada por vírgulas de nomes de host a serem excluídos do proxy. Não há suporte para asteriscos para curingas. Use um ponto (.) quando quiser corresponder a um subdomínio. Exemplos:NO_PROXY=.example.com
(com ponto à esquerda) corresponde awww.example.com
, mas não corresponde aexample.com
.NO_PROXY=example.com
(sem o ponto inicial) não corresponde awww.example.com
. Esse comportamento pode ser revisitado no futuro para corresponder melhor a outros ecossistemas.
Em sistemas nos quais as variáveis de ambiente diferenciam maiúsculas de minúsculas, os nomes de variáveis podem ter apenas minúsculas ou apenas maiúsculas. Os nomes em minúsculas são verificados primeiro.
O servidor proxy pode ser um nome de host ou endereço IP, seguido opcionalmente por dois pontos e um número de porta, ou pode ser uma URL http
, incluindo, se necessário, um nome de usuário e uma senha para autenticação do proxy. A URL deve começar com http
, não https
e não pode incluir nenhum texto após o nome do host, IP ou porta.
Configurar o proxy por 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 propriedade Proxy
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 é usado. Se a propriedade Proxy
for especificada, as configurações de proxy da propriedade Proxy
substituirão o arquivo de configuração de aplicativo ou computador local 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 ao servidor.
A classe HttpClientHandler analisa uma lista de bypass de proxy com caracteres curinga herdados das configurações do computador local. Por exemplo, a classe HttpClientHandler
interpreta uma lista de bypass de "nt*"
dos 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
dá suporte ao bypass de proxy local. A classe considera um destino como local se qualquer uma das seguintes condições for atendida:
- O destino contém um nome simples (sem períodos (.) na 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:
- Propriedade WebProxy.Address
- Propriedade WebProxy.BypassProxyOnLocal
- Propriedade WebProxy.BypassArrayList