HttpClient is used for sending HTTP requests and receiving HTTP responses from a resource identified by a URI. But, HttpClient has some issues. To read more on the issues of HttpClient, you can check this link. In the .NET Core 2.1 release, Microsoft has introduced a new way of designing HttpClients to solve these issues, and it's called HttpClientFactory. HttpClientFactory is an opinionated factory, available since .NET Core 2.1, for creating HttpClient instances in our applications. This means that we can create HttpClients and can register them in the HttpClientFactory in our application and can leverage the dependency injection capabilities of the .NET core to inject these HttpClients in our code. HttpClientFactory allows us to no longer care about the lifecycle of the HttpClient by leaving it to the framework.
 
There are three ways to use HttpClientFactory to instantiate HttpClients.

  • Default client
  • Named client
  • Typed client

In order to use the factory, we need to register it in the DI container. So, we need to use an extension method AddHttpClient() on IServiceCollection interface in our Startup.cs class. This will allow us to inject the HttpClient in our class constructors.
 
In this article, we will see how to create a Typed HttpClient using the HttpClient factory in a .NET core MVC application and use it for making HTTP calls. I prefer Typed HttpClient over the other two because,

    As the name suggests, typed clients provide type safety.
    Typed clients help in encapsulating the API calls when we are making use of the HttpClient at one place, thus making our code DRY (Don't Repeat Yourself). The other two will scatter the implementation details of making HTTP calls throughout the codebase.

We will make a simple MVC application to learn the workings of typed HttpClient. This application will receive the name of a movie and call a REST API to fetch the details of that movie and shall display it to the user. I will be using Visual Studio Code for developing the application. The REST API used for fetching the movie details is the OMDB API. The OMDB API is a RESTful web service to obtain movie information. This is a free API with a 1000 requests per day limit for a user. We need an API key for accessing this API. To know more details about this API you can check their website.

Create a folder called MovieFinder and open it in VS Code. Create an MVC application by running the following command in the terminal.
    dotnet new mvc --name MovieFinder 

This shall create a basic .NET Core MVC application. Now, let’s create a View Model class to hold the data from the OMDB API. So, let’s add a class named MovieDetailModel.
    public class MovieDetailModel 
    { 
        public string Title { get; set; } 
        public string Year { get; set; } 
        public string Director { get; set; } 
        public string Actors { get; set; } 
        public string IMDBRating { get; set; } 
        public string PosterImage { get; set; } 
        public string Plot { get; set; } 
    } 


Now, we need to create an interface for our typed client. Let's name it IMovieDetailsClient.
    public interface IMovieDetailsClient 
    { 
        Task<MovieDetailModel> GetMovieDetailsAsync(string movieName); 
    } 


This interface contains a single method, GetMovieDetailsAsync, which accepts the movie name as the parameter and shall return the details of that movie. Now we need to create a class which implements this interface. This class shall contain the actual logic of calling the OMDB API to fetch the movie details.
    public class MovieDetailsClient : IMovieDetailsClient 
    { 
        private readonly HttpClient _httpClient; 
     
        public MovieDetailsClient(HttpClient httpClient) 
        { 
            httpClient.BaseAddress = new Uri("http://www.omdbapi.com/"); 
            _httpClient = httpClient; 
        } 
     
        public async Task<MovieDetailModel> GetMovieDetailsAsync(string movieName) 
        { 
            var queryString = $"?t={movieName}&apikey=<your-api-key>"; 
            var response = await _httpClient.GetStringAsync(queryString); 
     
            JObject json = JObject.Parse(response); 
     
            if (json.SelectToken("Response").Value<string>() == "True") 
            { 
     
                var movieDetails = new MovieDetailModel 
                { 
                    Title = json.SelectToken("Title").Value<string>(), 
                    Year = json.SelectToken("Year").Value<string>(), 
                    Director = json.SelectToken("Director").Value<string>(), 
                    Actors = json.SelectToken("Actors").Value<string>(), 
                    IMDBRating = json.SelectToken("imdbRating").Value<string>(), 
                    PosterImage = json.SelectToken("Poster").Value<string>(), 
                    Plot = json.SelectToken("Plot").Value<string>() 
                }; 
     
                return movieDetails; 
            } 
     
            return new MovieDetailModel 
            { 
                Title = movieName 
            }; 
        } 
    } 


In this class, we inject the HttpClient in our class constructor and set the base address of our OMDB API endpoint. We also implement GetMovieDetailsAsync method declared in our interface. We called the OMDB API from our method and mapped the API response to our view model and returned it. I have used the JSON.NET library for parsing the response from the API.
Note that I have hardcoded the API Base address and the API key in the code. This is not a healthy practice. We shall always prefer moving these kinds of values to configuration files and shall read those values from the configuration files in our code.
 
We have now created our typed client. Now let's create the view for user interaction. In the Index.cshtml file in the Views\Home folder, replace the existing code with the following code.
    @{ 
        ViewData["Title"] = "Home Page"; 
    } 
     
    @model MovieDetailModel 
     
    <form  
        asp-controller="Home"  
        asp-action="Submit" 
        method="post"  
        class="form-horizontal"  
        role="form"> 
     
        <div class="form-group"> 
            <label for="Title">Title</label> 
            <input  
                class="form-control"  
                placeholder="Enter Title" 
                asp-for="Title">    
        </div> 
        <button type="submit" class="btn btn-primary">Submit</button> 
    </form> 
     
     
    @{ 
        <br> 
         
        if(!string.IsNullOrEmpty(Model?.Year)) 
        { 
            var title = Model.Title; 
            var year = Model.Year;   
            var message = $"{title} is release on {year}";  
      
            <br> 
            <div class="card" style="width: 18rem;"> 
                <img class="card-img-top" [email protected] alt="Poster Not Available"> 
                <div class="card-body"> 
                    <h5 class="card-title">@Model.Title (@Model.Year)</h5> 
                    <p class="card-text">@Model.Plot</p>               
                </div> 
                <ul class="list-group list-group-flush"> 
                    <li class="list-group-item"><strong>Director : @Model.Director</strong></li> 
                    <li class="list-group-item"><strong>Actors : @Model.Actors</strong></li> 
                    <li class="list-group-item"><strong>Rating : @Model.IMDBRating</strong></li> 
                </ul> 
            </div>      
        } 
         
        if(Model != null && string.IsNullOrEmpty(Model?.Year)) 
        { 
            <div class="alert alert-danger" role="alert"> 
                <strong>Sorry!! Requested Movie Details are not available..</strong>  
            </div> 
        } 
    } 


Now, we need to add the Controller code for accepting the movie name from the view and for displaying the movie details. For that, we need to inject the typed client we had created into the constructor of our controller. So, we need to register this typed client with the HttpClient factory in our Startup.cs class. Add the following code in the ConfigureServices method in the startup class.
    services.AddHttpClient<IMovieDetailsClient, MovieDetailsClient>(); 

Now, let's add our controller methods. In the HomeController make the changes as below.
    public class HomeController : Controller 
    { 
        private readonly IMovieDetailsClient _movieDetailsClient; 
     
        public HomeController(IMovieDetailsClient movieDetailsClient) 
        { 
            _movieDetailsClient = movieDetailsClient; 
        } 
     
        public IActionResult Index() 
        { 
            return View(); 
        } 
     
        [HttpPost] 
        public async Task<IActionResult> Submit(MovieDetailModel model) 
        { 
            var movieDetail = await _movieDetailsClient.GetMovieDetailsAsync(model.Title); 
            return View("Index", movieDetail); 
        }       
    } 


We are injecting our IMovieDetails client in the constructor of our controller and assigning it to a read-only field _movieDetailsClient. We have also defined an action named Submit which takes the title of the movie from the view as a parameter. This method makes use of our typed HttpClient to fetch the details of that movie and shall return the view with the details of that movie.

Now, run the application. Execute the command dotnet run in the terminal. Open a browser and navigate to https://localhost:5001/.