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:
- https://jsonplaceholder.typicode.com: Um site que fornece uma plataforma de API falsa gratuita para testes e prototipagem.
- https://www.example.com: Um domínio disponível para uso em exemplos ilustrativos em documentos.
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ávelstatic
. De acordo com as diretrizes , a abordagem recomendada é reutilizar 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:
- Defina a propriedade HttpClient.DefaultRequestHeaders.
- Aplique uma propriedade HttpClient.Timeout não padrão.
- Especifique a propriedade HttpClient.DefaultRequestVersion.
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étodoSendAsync
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).
- ByteArrayContent: Fornece conteúdo HTTP com base 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 com base em um fluxo.
- StringContent: Fornece conteúdo HTTP com base em uma cadeia de caracteres.
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çãoPOST
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 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 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çãoPUT
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 objetosTodo
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:
- Método HttpClient.GetByteArrayAsync
- Método HttpClient.GetStreamAsync
- Método HttpClient.GetStringAsync
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áveisHTTP_PROXY
e/ouHTTPS_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 awww.example.com
, mas não corresponde aexample.com
.NO_PROXY=example.com
(sem ponto inicial) não corresponde awww.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 https
e 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:
- WebProxy.Address propriedade
- Propriedade WebProxy.BypassProxyOnLocal
- WebProxy.BypassArrayList propriedade