Introdução: Por que Você Precisa de um Cliente HTTP Adequado em C#
No desenvolvimento moderno, quase nenhum aplicativo pode funcionar sem interagir com serviços externos. Seja enviando dados para um servidor, obtendo taxas de câmbio ou trabalhando com uma API na nuvem — tudo isso requer uma implementação adequada de requisições HTTP. No ecossistema C# (.NET), a principal ferramenta para esta tarefa é a classe HttpClient. No entanto, muitos desenvolvedores, especialmente iniciantes, cometem erros típicos: criar uma nova instância de HttpClient para cada requisição, esquecer de tratar exceções ou trabalhar incorretamente com assincronia.
Neste artigo, vamos detalhar como organizar corretamente um cliente HTTP em C#, verificar o envio de requisições GET e POST, trabalhar com cabeçalhos e corpo da requisição, além das melhores práticas para tratamento de erros. Após a leitura, você será capaz de escrever código confiável e eficiente para interagir com qualquer API REST.
Fundamentos do Trabalho com HttpClient em C#
A classe HttpClient faz parte do namespace System.Net.Http e está disponível em todas as versões modernas do .NET (Core, 5, 6, 7, 8 e superiores). Sua principal vantagem é o suporte a operações assíncronas e gerenciamento automático de pool de conexões.
Criando um HttpClient Corretamente
A regra mais importante: não crie um novo HttpClient para cada requisição. Isso pode levar ao esgotamento de sockets. Em vez disso, use uma única instância durante todo o ciclo de vida do aplicativo ou use IHttpClientFactory.
// ❌ Ruim: novo HttpClient para cada requisiçãousing (var client = new HttpClient()){ var response = await client.GetAsync("https://api.example.com");}
// ✅ Bom: um HttpClient para todas as requisiçõespublic class MyHttpService{ private static readonly HttpClient _client = new HttpClient(); public async Task<string> GetDataAsync(string url) { return await _client.GetStringAsync(url); }}Configuração Básica
Ao inicializar o HttpClient, você pode definir um endereço base, timeouts e cabeçalhos padrão:
public class ApiClient{ private readonly HttpClient _httpClient; public ApiClient() { _httpClient = new HttpClient { BaseAddress = new Uri("https://api.pythonlib.ru/"), Timeout = TimeSpan.FromSeconds(10) }; // Cabeçalhos que serão enviados em todas as requisições _httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0"); }}Enviando Requisições GET e POST
Vamos analisar os dois tipos mais comuns de requisições HTTP: obter dados (GET) e enviar dados (POST).
Requisição GET: Obtendo Dados
Uma requisição GET é usada para ler dados do servidor. Ela não deve alterar o estado do recurso e não contém corpo.
public async Task<string> GetUserAsync(int userId){ try { var response = await _httpClient.GetAsync($"/api/users/{userId}"); response.EnsureSuccessStatusCode(); // Lançará uma exceção em caso de erro return await response.Content.ReadAsStringAsync(); } catch (HttpRequestException ex) { Console.WriteLine($"Erro na requisição: {ex.Message}"); throw; }}Para casos simples, você pode usar a forma abreviada:
string json = await _httpClient.GetStringAsync("/api/users/42");Requisição POST: Enviando Dados
Uma requisição POST é usada para criar novos recursos. Os dados são enviados no corpo da requisição, geralmente no formato JSON.
public async Task<User> CreateUserAsync(User newUser){ // Serializa o objeto para JSON var json = JsonSerializer.Serialize(newUser);