Search code examples
c#httpclientapiclient

Custom API Client organization


I have a custom API Client calling a custom API Service with many controllers. I actually have this class:

public abstract class APIClientBase
{
    protected HttpClient _httpClient;

    public APIClientBase()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("https://localhost:44369/");
    }
}

which is implemented by many classes like this one:

public class APIClientEmployee : APIClientBase
{
    public async Task<ObservableCollection<Employee>> GetAllEmployeesRequestAsync()
    {
        ObservableCollection<Employee> employees = null;
        HttpResponseMessage response = await _httpClient.GetAsync($"api/employees");

        if (response.IsSuccessStatusCode)
        {
            employees = response.Content.ReadAsAsync<ObservableCollection<Employee>>().Result;
        }

        return employees;
    }

    public ObservableCollection<Employee> GetAllVeterinaryEmployeesRequest()
    {
        ObservableCollection<Employee> employees = null;
        HttpResponseMessage response = _httpClient.GetAsync($"api/employees/veterinaryemployees").Result;

        if (response.IsSuccessStatusCode)
        {
            employees = response.Content.ReadAsAsync<ObservableCollection<Employee>>().Result;
        }

        return employees;
    }

    public async Task<Employee> GetSingleEmployeeRequestAsync(int id)
    {
        Employee employee = null;
        HttpResponseMessage response = await _httpClient.GetAsync($"api/employees/{id}");

        if (response.IsSuccessStatusCode)
        {
            employee = response.Content.ReadAsAsync<Employee>().Result;
        }

        return employee;
    }

    public Employee GetSingleEmployeeRequest(int id)
    {
        Employee employee = null;
        HttpResponseMessage response = _httpClient.GetAsync($"api/employees/{id}").Result;

        if (response.IsSuccessStatusCode)
        {
            employee = response.Content.ReadAsAsync<Employee>().Result;
        }

        return employee;
    }

    public async Task PutSingleEmployeeRequestAsync(Employee employee)
    {
        HttpResponseMessage response = await _httpClient.PutAsJsonAsync($"api/employees/{employee.EmployeeID}", employee);
        response.EnsureSuccessStatusCode();
    }

    public async Task PostSingleEmployeeRequestAsync(Employee employee)
    {
        HttpResponseMessage response = await _httpClient.PostAsJsonAsync($"api/employees", employee);
        response.EnsureSuccessStatusCode();
    }

    public async Task DeleteSingleEmployeeRequestAsync(int id)
    {
        HttpResponseMessage response = await _httpClient.DeleteAsync($"api/employees/{id}");
        response.EnsureSuccessStatusCode();
    }
}

And I use them like this in my ViewModels:

 public class EmployeeListViewModel
 {
    private APIClientEmployee _apiClientEmployee;

    private ObservableCollection<Employee> _employees;

    public ObservableCollection<Employee> Employees
    {
        get
        {
            return _employees;
        }
        set
        {
            _employees = value;
            OnPropertyChanged("Employees");
        }
    }

    public EmployeeListViewModel()
    {
        _apiClientEmployee = new APIClientEmployee();
        FillEmployeeList();
    }

    public async void FillEmployeeList()
    {
        Employees = await _apiClientEmployee.GetAllEmployeesRequestAsync();
    }
}

I do something like this in a lot of places. Also, I create others APIClientEmployee in other ViewModels because I need Employee data in some others places. But this method makes me create many APIClient objects/Http client and I feel like there is something wrong in the way I do it. I think I could bust my socket limit ore something like that and then it would cause problems. I have readed many tutorials but they often are not complex. Should I have only one class with every API calls in it? Should I implement a singleton with one instance of each of my APIClient classes? I don't know what to do and I would like to have suggestions...


Solution

  • You are quite right. Check the official documentation on HttpClient:

    HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors. Below is an example using HttpClient correctly.

    This is how it is declared

    static readonly HttpClient client = new HttpClient();
    

    You can do the following:

    public abstract class APIClientBase
    {
        protected static readonly HttpClient client = new HttpClient();
    

    And then use like this:

    HttpResponseMessage response = await APIClientBase.client.GetAsync($"api/employees/{id}");