C# – Switch from using HttpWebRequest to HttpClient

There are many reasons to use HttpClient instead of HttpWebRequest. For one, the MSDN docs strongly recommends against using HttpWebRequest, and using HttpClient instead. That should be enough, but if you need a little more convincing, take a look at the Practical reasons to not use HttpWebRequest section below.

In addition to explaining why not to use HttpWebRequest, this article shows how to refactor code that uses HttpWebRequest to use HttpClient instead.

Practical reasons to not use HttpWebRequest

Here are four practical reasons to not use HttpWebRequest.

1 – Microsoft strongly recommends switching to HttpClient.

This warning is straight from the MSDN documentation regarding HttpWebRequest:

We don’t recommend that you use HttpWebRequest for new development. Instead, use the System.Net.Http.HttpClient class.

https://learn.microsoft.com/en-us/dotnet/api/system.net.httpwebrequest?view=net-8.0#remarks

2 – You can’t reuse the same HttpWebRequest object for multiple requests

If you try to reuse the same HttpWebRequest object, you get the following exception:

System.InvalidOperationException: ‘This operation cannot be performed after the request has been submitted.’

3 – Using HttpWebRequest can lead to port exhaustion problem

Every time you create a new HttpWebRequest and make a request, it allocates a socket on a new port. Not only is this terrible for performance, but it will eventually lead to port exhaustion. This can be seen by using netstat -an. I sent 6 requests using HttpWebRequest within 30 seconds, and here you can see there are 6 ports in the TIME_WAIT state.

  netstat -an | find "44388"

  TCP    0.0.0.0:44388          0.0.0.0:0              LISTENING
  TCP    [::]:44388             [::]:0                 LISTENING
  TCP    [::1]:13095            [::1]:44388            TIME_WAIT
  TCP    [::1]:13097            [::1]:44388            TIME_WAIT
  TCP    [::1]:13098            [::1]:44388            TIME_WAIT
  TCP    [::1]:13099            [::1]:44388            TIME_WAIT
  TCP    [::1]:13102            [::1]:44388            TIME_WAIT
  TCP    [::1]:13103            [::1]:44388            TIME_WAIT
Code language: plaintext (plaintext)

4 – It’s tedious to use

Compare the HttpWebRequest code with the HttpClient code in the sections below. HttpClient is much simpler to use in common scenarios, such as sending a request and looking at the response content.

In constrast, HttpClient solves all of these problems

HttpClient is simple to use. You can quite easily add request headers, query strings, handle JSON, and other common scenarios. Furthermore, HttpClient was specifically designed to handle multiple concurrent requests in an efficient manner. It does this by reusing open connections, which leads to better performance and avoids the port exhaust problem.

Code that uses HttpWebRequest

The following code uses HttpWebRequest to POST a message to an endpoint. The endpoint returns a response message.

This shows typical usage of HttpWebRequest. It creates the HttpWebRequest object, sets some headers, writes the request content stream, then parses the response content stream.

I put comments for each distinct step that it’s doing, and put the same comments in the equivalent steps in the refactored code that uses HttpClient. This should make it easier to see the difference between using HttpWebRequest and HttpClient.

public class MessageSender
{
	string url = "https://localhost:44388/message";
	public async Task<string> PostMessage(string jsonMessage)
	{

		//Create the request sender object
		var request = WebRequest.Create(url) as HttpWebRequest;
		
		//Initialize the request content
		var contentBuffer = Encoding.UTF8.GetBytes(jsonMessage);
		request.ContentType = "application/json";
		request.ContentLength = contentBuffer.Length;

		//Set the headers
		request.UserAgent = "MessageService/3.1";

		//Send the POST
		request.Method = "POST";
		using (var requestStream = request.GetRequestStream())
		{
			requestStream.Write(contentBuffer, 0, contentBuffer.Length);
			requestStream.Flush();
			requestStream.Close();
		}

		//Get the response status and content
		using (var httpResponse = await request.GetResponseAsync() as HttpWebResponse)
		{
			//Check for error status
			if (httpResponse.StatusCode != HttpStatusCode.OK)
			{
				throw new HttpRequestException(httpResponse.StatusDescription);
			}

			using (var responseStream = httpResponse.GetResponseStream())
			{
				using (var streamReader = new StreamReader(responseStream))
				{
					return streamReader.ReadToEnd();
				}
			}
		}
	}
}
Code language: C# (cs)

Refactored code that uses HttpClient instead of HttpWebRequest

Here is refactored code that uses HttpClient.

As you can see, HttpClient abstracts away a lot of the tedious stuff you had to do with HttpWebRequest.

using System.Net.Http;

public class MessageSender : IDisposable
{
	private readonly HttpClient httpClient;
	string url = "https://localhost:44388/message";
	public MessageSender()
	{
		//Create the request sender object
		httpClient = new HttpClient();

		//Set the headers
		httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("MessageService/3.1");

	}
	public async Task<string> PostMessage(string jsonMessage)
	{
		/* Initialize the request content 
		   and 
		   Send the POST
		*/
		var response = await httpClient.PostAsync(url, new StringContent(jsonMessage, Encoding.UTF8, "application/json"));

		//Check for error status
		response.EnsureSuccessStatusCode();

		//Get the response content
		return await response.Content.ReadAsStringAsync();
	}
	public void Dispose()
	{
		httpClient?.Dispose();
	}
}
Code language: C# (cs)

I left the comments from the original code so you can easily compare the equivalent distinct steps when using HttpClient compared with HttpWebRequest.

Let’s go through each comment.

  • Create the request sender object

The first major difference is that you only need to create one HttpClient object and reuse it repeatedly.

It implements IDisposable, but you only need to dispose it after you are completely done sending requests. Do not dispose the HttpClient instance if you intend on sending more requests. Do not use HttpClient in a using block.

  • Set the headers

As you can see in the code, I am sending the same header (UserAgent) with the same value every time. It makes sense to simply set httpClient.DefaultRequestHeaders once in the constructor. Of course you can set the headers in every request if they change every time. I didn’t need to do that in my use case, so that’s not shown here.

  • Initialize the request content

This is simply creating a StringContent object, specifying that it’s using UTF8 encoding, and setting the content type to “application/json”.

  • Send the POST

This is a one liner. It’s simply calling await httpClient.PostAsync(url, content). In fact, initializing the content and sending POST have been reduced to a single line. Compare that with the HttpWebRequest code and you see that it’s much simpler and less tedious. Note: There are extension methods that make sending JSON with HttpClient even simpler.

  • Check for error status

This, too, has been reduced to a one liner. Just call response.EnsureSuccessStatusCode(). This’ll throw an HttpRequestException if the status code isn’t a 200 (OK).

  • Get the response content

This has also been reduced to a one liner. It simply calls await response.Content.ReadAsStringAsync(). This greatly reduces the tediousness of parsing the response.

2 thoughts on “C# – Switch from using HttpWebRequest to HttpClient”

Leave a Comment