Examples of Repository Pattern with consuming an external REST web service via HttpClient?

18,942

A simple example:

// You need interface to keep your repository usage abstracted
// from concrete implementation as this is the whole point of 
// repository pattern.
public interface IUserRepository
{
    Task<User> GetUserAsync(int userId);
}

public class UserRepository : IUserRepository
{
    private static string baseUrl = "https://example.com/api/"

    public async Task<User> GetUserAsync(int userId)
    {
        var userJson = await GetStringAsync(baseUrl + "users/" + userId);
        // Here I use Newtonsoft.Json to deserialize JSON string to User object
        var user = JsonConvert.DeserializeObject<User>(userJson);
        return user;
    }

    private static Task<string> GetStringAsync(string url)
    {
        using (var httpClient = new HttpClient())
        {
            return httpClient.GetStringAsync(url);
        }
    }
}

Here is where/how to get Newtonsoft.Json package.


Another option would be to reuse HttpClient object and make your repository IDisposable because you need to dispose HttpClient when you done working with it. In my first example it happens right after HttpClient usage at the end of using statement.

Share:
18,942

Related videos on Youtube

Jiveman
Author by

Jiveman

Updated on June 03, 2022

Comments

  • Jiveman
    Jiveman almost 2 years

    I've searched around quite a bit, but haven't found any good examples of consuming an external REST web service using a Repository Pattern in something like an ASP.NET MVC app, with loose coupling and meaningful separation of concerns. Almost all examples of repository pattern I find online are writing SQL data or using an ORM. I'd just like to see some examples of retrieving data using HttpClient but wrapped in a repository.

    Could someone write up a simple example?

  • Jiveman
    Jiveman almost 8 years
    Ah I see! And if I wanted to also be able to retrieve user by email address, I would add public async Task<User> GetUserAsync(string email) {} with slightly different implementation than with integer id. What if I also needed to be able to get other types of stuff from the same web service (user, role, product, etc.)? Would it make sense to extract the HttpClient stuff into a wrapper class of some kind, and leave repository to be specific per entity (UserRepository, RoleRepository, ProductRepository)?
  • Andrei
    Andrei almost 8 years
    @Jiveman yes repositories, specific for entities make perfect sense. If you have something common for all repositories, for instance generic method that performs GET request, you can put it in abstract BaseRepository. Also food for thought is unit testing repository. In this case you indeed need to wrap your HttpClient into easily mockable class and inject it in repos as a dependency.
  • Jiveman
    Jiveman almost 8 years
    Yes, unit testing is definitely important! Thank you for mentioning that. OK, this definitely helps me think about things in a better way.
  • Jiveman
    Jiveman almost 8 years
    And, btw, I definitely like the idea of reusing HttpClient, as it's not really supposed to be disposed on every use.
  • Andrei
    Andrei almost 8 years
    @Jiveman depends on how you are going to use repositories. In many scenarios there is only one call that needs to be performed. So you need to decide either to make repository disposable and every time you use repo, you wrap it with using (...) {} statement or create/dispose http client internally every time you do HTTP call with benefit of keeping repository API straightforward and with no potential memory leaks when developers forget to dispose resources. Either solution is absolutely acceptable, it's your call! :)
  • user510101
    user510101 about 7 years
    as Andrei and @Jiveman have mentioned: DO NOT create a new HttpClient and dispose with a using stmt each time you need it. The HttpClient is designed to be reused for the scope of APPLICATION. So you should either use an IoC container with a singleton scope, or create a static HttpClient instance, and it injected into your repository. otherwise your app will begin leaving open TCP sockets and server/machine performance will begin tanking.
  • Howiecamp
    Howiecamp almost 7 years
    @Andrei What about using the pattern for things that are not data access? For example maybe I'm building a text to speech service. There are 3 vendors I can use, each with their own rest api. So I create an ITTSService interface and let's say give it void ConvertToSpeech() and int VoiceType members. Then I'd create concrete representations of these, specific to each of the 3 provider's APIs. I guess this is just an example of using interfaces, but then again so is the repo pattern! Is this example an appropriate use of the pattern?
  • Andrei
    Andrei almost 7 years
    @Howiecamp indeed in your scenario interface for ISpeech[Something] works perfectly. It will give you flexibility of substituting implementations and injecting it as dependency in unit tests. As you noticed your component doesn't provide access to data, hence Repository name doesn't quite describe the purpose of your class. My recommendation would be to call your interface ISpeechService and implementations - Concrete1Service, Concrete2Service etc. Good luck!
  • arjun
    arjun almost 4 years
    It is a bad idea to create httpclient object every time you make an API call. Because even if you use using statement the socket might not get released and eventually you end up getting socket exhaustion exception.Instead use IHttpClientFactory to create httpclient.
  • O.MeeKoh
    O.MeeKoh over 2 years
    This looks like a nice approach, but what if the API you are using inside your repository doesn't just return data for User, but also for company info, company products. Would you create separate Repositories for these end points even though they are all coming from the same third party API?
  • Andrei
    Andrei over 2 years
    @O.MeeKoh it's fine if you're calling a small number of different HTTP APIs. But if you have too many - separating might be a good idea.
  • O.MeeKoh
    O.MeeKoh over 2 years
    In such case, a single Repository that represents multiple end points would return different type of models. I'm guessing thats fine.
  • Andrei
    Andrei over 2 years
    @O.MeeKoh I think so too.