Generic HttpClient wrapper in C#

Create a Generic REST Client in C#

In this article, we will explore how to create a generic asynchronous RESTful HttpClient that can be used with any service. This class will simplify the process of creating RESTful clients that interact with RESTful services. It is especially useful if you need to build a RESTful cross-domain client. Let’s get started!

Why Do We Need a Generic Client?

When working with RESTful services, we often need to interact with multiple resources that have different types and identifiers. Instead of creating separate client classes for each resource, a generic client provides a flexible and reusable solution. By using a generic REST client, we can avoid code duplication and reduce maintenance effort.

The RestClient Class

First, let’s create a generic class called RestClient<TResource, TIdentifier> that implements the IDisposable interface and accepts two type parameters: TResource and TIdentifier. This class will serve as our REST client.

RestClient.cs

Create a generic class with two-parameter TResource and TIdentifier

public class RestClient<TResource, TIdentifier> : IDisposable where TResource : class
{


	private HttpClient httpClient;
	protected readonly string _baseAddress;
	private readonly string _addressSuffix;
	private bool disposed = false;

	public RestClient(string baseAddress, string addressSuffix)
	{
		_baseAddress = baseAddress;
		_addressSuffix = addressSuffix;
		httpClient = CreateHttpClient(_baseAddress);
	}
	protected virtual HttpClient CreateHttpClient(string serviceBaseAddress)
	{
		httpClient = new HttpClient();
		httpClient.BaseAddress = new Uri(serviceBaseAddress);
		return httpClient;
	}
	public async Task<TResource> GetAsync(TIdentifier identifier)
	{
		var responseMessage = await httpClient.GetAsync(_addressSuffix + identifier.ToString());
		responseMessage.EnsureSuccessStatusCode();
		return await responseMessage.Content.ReadAsAsync<TResource>();
	}
	public async Task<IEnumerable<TResource>> GetAll()
	{
		var responseMessage = await httpClient.GetAsync(_addressSuffix);
		responseMessage.EnsureSuccessStatusCode();
		return await responseMessage.Content.ReadAsAsync<IEnumerable<TResource>>();
	}

	public void Dispose()
	{
		Dispose(true);
		GC.SuppressFinalize(this);
	}

	private void Dispose(bool disposing)
	{
		if (!disposed && disposing)
		{
			if (httpClient != null)
			{
				httpClient.Dispose();
			}
			disposed = true;
		}
	}
}

Sequence Diagram

Now, let’s visualize the interaction between the generic REST client and the RESTful service using a Mermaid sequence diagram:

ClientRestClientRESTful  ServiceCreate instanceInvoke methodSend HTTP requestSend HTTP responseReturn responseClientRestClientRESTful  Service

The TodoClient Class

Now, let’s create a class called TodoClient that utilizes the generic RestClient to interact with the “todos” resource. In this example, we’ll use the Todo class as the resource type and string as the identifier type.

public class Todo
{
	public int userId { get; set; }
	public int id { get; set; }
	public string title { get; set; }
	public bool completed { get; set; }
}

public class TodoClient
{
	private readonly RestClient<Todo, string> _restClient;
	private readonly string baseAddress = "https://jsonplaceholder.typicode.com/";
	public TodoClient()
	{
		_restClient = new RestClient<Todo, string>(baseAddress, "todos/");
	}
	public async Task<Todo> Get(string id)
	{
		var result = await _restClient.GetAsync(id);
		return result;
	}
	public async Task<IEnumerable<Todo>> GetAll()
	{
		var result = await _restClient.GetAll();
		return result;
	}


}

How to Use the Generic Client

To use the generic REST client, create an instance of the specific client, such as TodoClient, and invoke its methods. Here’s an example of how to use the TodoClient:


void Main()
{
	var todoClient = new TodoClient();
	var todo = todoClient.Get("1");
	Console.WriteLine(todo);
	var todos = todoClient.GetAll();
	Console.WriteLine(todos);


}

3 Comments

Please do not post any spam link in the comment box😊

  1. Hi,

    This is nice

    This would be perfect if you could add authentication using a Bearer and Refresh token

    ReplyDelete
  2. Are you sure that method Dispose() works?

    ReplyDelete
Post a Comment
Previous Post Next Post