Introduction: Why You Need a Proper HTTP Client in C#
In modern .NET development, almost no application can function without interacting with remote servers. Whether it's fetching data from a REST API, sending forms to a web server, or downloading files — all of this requires proper implementation of HTTP requests. In C#, there are several approaches, but the most modern and recommended one is the HttpClient class from the System.Net.Http namespace. In this article, we will break down how to properly create an HTTP client in C#, avoid common mistakes, and write efficient code.
Main Classes for Working with HTTP in C# (.NET)
Before moving to examples, it's important to understand what tools the .NET platform provides. The main classes are:
- HttpClient — the main class for sending requests and receiving responses. It should be used as a singleton (one instance for the entire application).
- HttpRequestMessage — allows detailed configuration of the request: method, headers, body, protocol version.
- HttpResponseMessage — contains the response from the server: status, headers, body.
- HttpContent — a base class for representing the request or response body (e.g.,
StringContent,ByteArrayContent,MultipartFormDataContent).
The key rule: do not create a new HttpClient instance for each request. This leads to socket exhaustion and performance degradation. Use one static or injected instance.
Configuring HttpClient: Basic Example
Let's look at a minimal configuration. We'll create an HttpClient instance and set a base address:
using System;using System.Net.Http;using System.Threading.Tasks;
public class ApiService{ private static readonly HttpClient client = new HttpClient();
public ApiService() { client.BaseAddress = new Uri("https://api.example.com/"); client.DefaultRequestHeaders.Add("User-Agent", "C# HttpClient App"); client.DefaultRequestHeaders.Accept.Add( new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); }
public async Task<string> GetDataAsync(string endpoint) { HttpResponseMessage response = await client.GetAsync(endpoint); response.EnsureSuccessStatusCode(); // Throws an exception on error return await response.Content.ReadAsStringAsync(); }}Note the response.EnsureSuccessStatusCode() — this is a simple way to check that the request was successful (2xx status code).
Practical Examples of HTTP Requests in C#
Now let's move to specific scenarios. We'll cover GET and POST requests, as well as working with headers and error handling.
1. GET Request with Parameters
To pass parameters in the query string, you can use the QueryHelpers collection from Microsoft.AspNetCore.WebUtilities or simply form the URL manually. Below is an example with manual formation:
public async Task<string> SearchUsersAsync(string name, int page){ var query = $"/users?name={Uri.EscapeDataString(name)}&page={page}"; HttpResponseMessage response = await client.GetAsync(query); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync();}Always use Uri.EscapeDataString() to escape parameters to avoid errors with special characters.
2. POST Request with JSON Body
For sending data in JSON format, it's convenient to use the System.Text.Json library (or Newtonsoft.Json). We'll convert the object to JSON and send it:
using System.Text;using System.Text.Json;
public