HttpClient and RestSharp are HTTP Client libraries that we can use to consume APIs. Working within the domain of Web Development, we will find ourselves in a situation where we need to consume external APIs. Both HttpClient and RestSharp are tools for implementing communication between APIs.

To download the source code for this article, you can visit our GitHub repository.

Let’s start.

What is HttpClient?

HttpClient is a class available within the .NET environment. We use the HttpClient class to send HTTP requests and receive HTTP responses based on the URI. HttpClient can process multiple concurrent requests.

What is RestSharp?

RestSharp is an open-source HTTP Client library that we can use to consume APIs. Based on that, we can install it using NuGet Package Manager. Although RestSharp can call any API using the HTTP protocol, the purpose of RestSharp is to consume the REST APIs. RestSharp supports both synchronous and asynchronous requests.

HttpClient vs RestSharp Benchmark

Now, let’s start by comparing HttpClient and RestSharp using HTTP methods to inspect time performance and memory usage for each tool. Firstly, we will use the BenchmarkDotNet library to get the benchmark results. Secondly, we will use JSONPlaceholder instead of creating a new API from scratch. JSONPlaceholder is a free API that we can use for testing purposes.

The time performance is a valuable factor in comparing HttpClient and RestSharp when consuming APIs. As a result, we can conclude how fast we can execute the identical requests using each tool. In this example, we will display the numbers in milliseconds for each request. We will use GET, POST, PUT and DELETE operations.

First, let’s create aĀ TodoBenchmark class where we will have all of our methods:

[MemoryDiagnoser]
public class TodoBenchmark
{
    private static readonly HttpClient httpClient = new HttpClient();
    private static readonly RestClient restClient = new RestClient("https://jsonplaceholder.typicode.com/");

    public TodoBenchmark()
    {
        httpClient.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
    }
}

In this class, we initialize two static objects. The first one is httpClient, which we will use to consume the API using the HttpClient tool. We call the second one restClient for consuming API calls with RestSharp. In both cases, we set the base URI, which is the same for each tool. We use the MemoryDiagnoser class annotation to get memory allocation results for each API call.

Before testing, we will create aĀ Todo class to use when calling the API:

public class Todo
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public string? Title { get; set; }
    public bool Completed { get; set; }
}

GET Method Memory and Time Performance

To show how we can measure the time and memory performance of the GET request, we are going to add one method for each tool:

[Benchmark]
public async Task<List<Todo>?> GetAllTodos_HttpClient()
{
    var response = await httpClient.GetAsync("todos");
    var todos = await response.Content.ReadFromJsonAsync<List<Todo>>();

    return todos;
}

[Benchmark]
public async Task<List<Todo>?> GetAllTodos_RestSharp()
{
    var request = new RestRequest("todos");
    var todos = await restClient.GetAsync<List<Todo>>(request);

    return todos;
}

We create theĀ GetAllTodos_HttpClient andĀ GetAllTodos_RestSharp methods and place a Benchmark annotation on each method so we can get results later using the BenchmarkDotNet library. After setting the base URI in the previous step, we add a relative path in each example to retrieve the list of Todo objects. After that, we execute an API call and deserialize the return value into a list ofĀ Todo objects calledĀ todos.

In theĀ GetAllTodos_HttpClientĀ method, we use theĀ GetAsyncĀ andĀ ReadFromJsonAsyncĀ methods to get the response and deserialize the data. There is another approach for fetching and deserializing data with HttpClient. We can use theĀ GetFromJsonAsyncĀ generic method to get the response and deserialize the data in one line. However, our first approach turned out to be slightly faster, though not noticeably so. Based on that, if performance is not extremely important, we can use the second approach to reduce lines of code.

Lastly, let’s execute the benchmark in the main program with the command:

BenchmarkRunner.Run<TodoBenchmark>();

Then, we run the project in the release configuration dotnet run -c Release, and get the results:

|                 Method |     Mean |    Error |   StdDev |   Median | Allocated |
|----------------------- |---------:|---------:|---------:|---------:|----------:|
| GetAllTodos_HttpClient | 53.70 ms | 1.556 ms | 4.465 ms | 52.12 ms |     94 KB |
|  GetAllTodos_RestSharp | 44.56 ms | 2.171 ms | 6.264 ms | 43.42 ms |    346 KB |

The results are similar when comparing the mean time required for call executions. On the other hand, when comparing memory allocation, we can attribute a significant advantage to HttpClient.

POST Method Memory and Time Performance

In this section, we will do the same steps for the POST request to create a Todo object with HttpClient and RestSharp libraries.

First, we will create a private helper method for getting a new Todo object:

private static Todo GetTodoForCreation()
{
    return new Todo
    {
        Id = 0,
        UserId = 1,
        Completed = false,
        Title = "Wake Up!"
    };
}

Then, we will add CreateTodo_HttpClient and CreateTodo_RestSharp methods:

[Benchmark]
public async Task<Todo?> CreateTodo_HttpClient()
{
    var todoForCreation = GetTodoForCreation();
    var response = await httpClient.PostAsJsonAsync("todos", todoForCreation);
    var createdTodo = await response.Content.ReadFromJsonAsync<Todo>();
    return createdTodo;
}
[Benchmark]
public async Task<Todo?> CreateTodo_RestSharp()
{
    var todoForCreation = GetTodoForCreation();
    var request = new RestRequest("todos").AddJsonBody(todoForCreation);
    var createdTodo = await restClient.PostAsync<Todo>(request);
    return createdTodo;
}

Nothing new here. Initially, we initialize theĀ todoForCreationĀ variable using a previously implemented method. Then, we execute the API calls with both libraries. We call the API with theĀ PostAsyncĀ method for RestSharp and theĀ PostAsJsonAsyncĀ method for HttpClient. We use theĀ PostAsJsonAsyncĀ method to serialize and send the request with one command. Once that is done, we deserialize the response into theĀ TodoĀ object to return.

Lastly, we run the benchmark again to get the results:

|                Method |     Mean |   Error |  StdDev | Allocated |
|---------------------- |---------:|--------:|--------:|----------:|
| CreateTodo_HttpClient | 251.9 ms | 4.88 ms | 5.42 ms |     43 KB |
|  CreateTodo_RestSharp | 253.1 ms | 4.99 ms | 6.32 ms |    109 KB |

In this case, execution times are almost identical within two milliseconds, while RestSharp still takes significantly more memory.

PUT Method Memory and Time Performance

Next in line is the PUT method. In this case, we will update the Todo object using HttpClient and RestSharp.Ā 

Again, we will create a static method for getting a Todo object used for the update operation:

private static Todo GetTodoForUpdate()
{
    return new Todo
    {
        Id = 2,
        UserId = 2,
        Completed = false,
        Title = "Have Lunch!"
    };
}

In the next step, let’s implement two methods for updating the Todo object through the API:

[Benchmark]
public async Task<Todo?> UpdateTodo_HttpClient()
{
    var todoForUpdate = GetTodoForUpdate();

    var serializedTodo = JsonSerializer.Serialize(todoForUpdate);
    var requestContent = new StringContent(serializedTodo, Encoding.UTF8, "application/json");

    var response = await httpClient.PutAsync($"todos/{todoForUpdate.Id}", requestContent);
    var updatedTodo = await response.Content.ReadFromJsonAsync<Todo>();

    return updatedTodo;
}

[Benchmark]
public async Task<Todo?> UpdateTodo_RestSharp()
{
    var todoForUpdate = GetTodoForUpdate();

    var request = new RestRequest($"todos/{todoForUpdate.Id}").AddJsonBody(todoForUpdate);
    var updatedTodo = await restClient.PutAsync<Todo>(request);

    return updatedTodo;
}

Like in the POST methods, we first retrieve the Todo object for the update operation and serialize it in JSON format. After that, we call the PutAsync method and deserialize the return value into the updatedTodo variable.

Let’s have a look at the output result:

|                Method |     Mean |   Error |   StdDev | Allocated |
|---------------------- |---------:|--------:|---------:|----------:|
| UpdateTodo_HttpClient | 254.0 ms | 3.93 ms |  4.82 ms |     10 KB |
|  UpdateTodo_RestSharp | 250.1 ms | 4.95 ms | 10.65 ms |     93 KB |

We can see very similar execution times to POST requests we compared before, with a slightly better time for RestSharp in this case. As usual, HttpClient is better at memory allocation.

DELETE Method Memory and Time Performance

Let’s add the last methods to show how we can test a DELETE request performance:

[Benchmark]
public async Task DeleteTodo_HttpClient()
{
    await httpClient.DeleteAsync("todos/1");
}

[Benchmark]
public async Task DeleteTodo_RestSharp()
{
    var request = new RestRequest("todos/1");
    await restClient.DeleteAsync(request);
}

Both methods are simple when we compare them with the POST and PUT methods from the previous sections. Within the DELETE call, we don’t need to send a JSON object and return deserialized value.

After that, we can check our results:

|                Method |     Mean |   Error |  StdDev | Allocated |
|---------------------- |---------:|--------:|--------:|----------:|
| UpdateTodo_HttpClient | 254.1 ms | 4.78 ms | 8.24 ms |     10 KB |
|  UpdateTodo_RestSharp | 258.8 ms | 4.87 ms | 8.65 ms |     85 KB |

At last, we can see that the execution time is almost identical on the DELETE operation for both HttpClient and RestSharp.

Benchmark Summary

Among the methods, we notice a pattern that implies that HttpClient and RestSharp are almost identical regarding the execution time. However, when it comes to memory allocation, HttpClient is significantly better.

HttpClient and RestSharp Behind the Scenes

Now, let’s look at the differences between HttpClient and RestSharp in the background. The most obvious difference is that HttpClient is already built into the framework. On the contrary, RestSharp is a third-party library that we need to install before using it.

HttpClient Background

An HttpClient object acts like a session that we can use to send HTTP requests and receive HTTP responses. We can describe it as a collection of settings applied to all of its requests. Furthermore, the HttpClient instance uses its connection pool for request isolation. As opposed to the RestSharp, HttpClient supports only async methods.

HttpClient implements an IDisposable interface. However, we shouldn’t explicitly dispose of it in most cases. When we dispose of the HttpClient, we close the underlying connection. With that, we need to re-open the connection for each subsequent request. Re-opening the connection is a costly process that can cause socket exhaustion. We can prevent this issue with HttpClientFactory to create and manage HttpClient instances.

Since .NET Core 2.1, HttpClient uses SocketsHttpHandler. Before that, HttpClientHandler was used in .NET Framework, .NET Core 2.0, and earlier. With that, SocketsHttpHandler introduced several advantages:

  • Performance improvement
  • Consistent behavior throughout all .NET platforms

RestSharp Background

Similarly, RestSharp is not overly complicated to use when consuming external APIs. It is a library that is easy to use across multiple platforms. We can say that RestSharp is probably the most popular HTTP client library for .NET among third-party libraries.

Unlike HttpClient, RestSharp supports synchronous and asynchronous methods. Another great feature that RestSharp offers is automatic JSON, XML, and custom serialization and deserialization. A breaking change inside the latest version is that RestSharp stops using the legacy HttpWebRequest class and uses HttpClient instead.

If we dive into the RestSharp RestClient class implementation, we can find HttpClient private getter:

private HttpClient HttpClient
{
    get;
}

That means that RestSharp is a wrapper around the HttpClient, which explains why RestSharp needs to allocate more memory when performing the API calls. Finally, in the release v107, SimpleJson class is completely removed from the code base. Instead, RestSharp uses JsonSerializer, the default serializer in .NET Core. Since RestSharp uses the HttpClient, we should consider similar issues regarding the RestClient instantiation. With the continuous creation of RestClient, we will get hanging connections and eventually the socket exhaustion.

With the new version implemented, RestSharp presumably solves a couple of issues:

  • Socket exhaustion
  • Hanging connections
  • SimpleJson serialization issues
  • HTTP/2 support

HttpClient vs RestSharp Pros and Cons

Finally, let’s have a look at the pros and cons of HttpClient and RestSharp.Ā 

HttpClient Pros and Cons

First, we will analyze the HttpClient library:

HttpClient ProsHttpClient Cons
Already built into the frameworkArguably harder to use when comparing it to the RestSharp
Better handling of memory allocationThe need for a custom implementation using HttpClientFactory class
A proven way to consume APIs in .NETLack of automatic serialization and deserialization
Significant improvement in performance and consistency
after the introduction of the SocketsHttpHandler class
Lack of support for synchronous API calls

RestSharp Pros and Cons

Again, let’s have a look at the pros and cons of using RestSharp:

RestSharp ProsRestSharp Cons
Like most libraries, it is easier to use because the hard work
is already done with some frequent issues eliminated
Additional bloat to a project when adding a third-party library
Similar time performance with HttpClientPoor memory allocation optimization
Automatic serialization and deserialization with JsonSerializerIt is based on the HttpClient itself, acting as a wrapper
Support of both synchronous and asynchronous API calls

Conclusion

In this article, we’ve learned quite a lot about HttpClient and RestSharp. We’ve inspected memory and time performance for both libraries. Our performance analysis shows that both libraries are similar in time performance. However, HttpClient performs better regarding memory allocation.

The main conclusion is that one is not better than the other, and we shouldn’t compare them since RestSharp is a wrapper around HttpClient. The decision between using one of the two tools depends on the use case and the situation. Not only do they perform similarly, but any difference is also much smaller than the wait on the network, so we cannot go wrong.