Introdução: por que um cliente HTTP correto é importante em C#
No desenvolvimento moderno em C# (.NET), trabalhar com requisições HTTP é uma necessidade básica. Seja para acessar uma REST API, baixar dados de um servidor web ou integrar com serviços em nuvem — um cliente HTTP confiável é indispensável. No entanto, muitos desenvolvedores, especialmente iniciantes, cometem erros típicos: gerenciamento incorreto de conexões, ignorar timeouts ou tratamento ineficiente de respostas. Neste artigo, vamos abordar a criação de um cliente HTTP em C#, desde o básico até técnicas avançadas.
Vamos examinar a classe HttpClient — a principal ferramenta para trabalhar com HTTP no .NET, bem como abordagens alternativas. Você aprenderá como enviar requisições GET e POST corretamente, processar JSON, gerenciar cabeçalhos e evitar problemas comuns de desempenho.
Fundamentos: criação e configuração do HttpClient
Inicialização correta do HttpClient
A classe HttpClient foi projetada para reutilização. Criar uma nova instância para cada requisição é um antipadrão que pode levar ao esgotamento de sockets. Em vez disso, use uma única instância para toda a aplicação ou injeção de dependência (DI) através de IHttpClientFactory.
// Incorreto: criar para cada requisiçãopublic async Task<string> BadRequest(string url){ using (var client = new HttpClient()) { return await client.GetStringAsync(url); }}
// Correto: instância estáticapublic static readonly HttpClient SharedClient = new HttpClient();
// Ou através de IHttpClientFactory (recomendado para ASP.NET Core)services.AddHttpClient("MyClient", client =>{ client.BaseAddress = new Uri("https://api.example.com"); client.DefaultRequestHeaders.Add("Accept", "application/json");});Configuração de timeouts e endereço base
Timeouts protegem a aplicação contra travamentos quando o servidor está indisponível. O HttpClient tem seu próprio timeout, mas pode ser sobrescrito.
HttpClient client = new HttpClient{ BaseAddress = new Uri("https://jsonplaceholder.typicode.com"), Timeout = TimeSpan.FromSeconds(10) // 10 segundos para toda a requisição};Também é útil definir MaxResponseContentBufferSize para limitar o tamanho dos dados recebidos.
Envio de requisições: GET, POST, PUT e DELETE
Requisição GET: obter dados
A maneira mais simples de obter dados é GetStringAsync ou GetAsync com tratamento manual.
public async Task<string> GetUsersAsync(){ HttpResponseMessage response = await SharedClient.GetAsync("/users"); response.EnsureSuccessStatusCode(); // lança exceção em caso de erro return await response.Content.ReadAsStringAsync();}
// Ou ainda mais simples:string json = await SharedClient.GetStringAsync("/users");Requisição POST: enviar dados
Para enviar JSON, use PostAsJsonAsync (requer o pacote System.Net.Http.Json).
var newUser = new { name = "João", email = "joao@example.com" };HttpResponseMessage response = await SharedClient.PostAsJsonAsync("/users", newUser);if (response.IsSuccessStatusCode){ var createdUser = await response.Content.ReadFromJsonAsync<User>();}Para enviar formulários, use FormUrlEncodedContent.
var formData = new Dictionary<string, string>{ { "username", "admin" }, { "password", "secret" }};var content = new FormUrlEncodedContent(formData);HttpResponseMessage response = await SharedClient.PostAsync("/login", content);Requisições PUT e DELETE
Atualização e exclusão são feitas de forma semelhante:
// PUTvar updateData = new { id = 1, name = "Nome atualizado" };HttpResponseMessage putR