Web requests
.NET Core C# HTTP REST Visual Studio Web API

Using IHttpClientFactory for Web API HTTP GET REST Calls

Welcome to today’s post.

In this post I will be discussing the approaches I have taken to use HTTP REST calls from within .NET Core applications. This can also include calls from web API service applications to other external web API services.

In this post I will be focusing on the execution of HTTP GET calls from within a .NET Core web API to another web API.

Before I cover how the calls are made though .NET Core, I will explain in the next section the HttpClientFactory and how useful it is for both inter-API HTTP REST calls and web client to API HTTP REST calls.

Explaining the HTTP Extensions Packages

I will be utilizing the Microsoft http extensions package to use the IHttpClientFactory provider service.

Instances of the HttpClient are managed and pooled by IHttpClientFactory.

The use of IHttpClientFactory is introduced in the Microsoft article.

There should be only one instance of HttpClient for each application.

Creation of multiple instances within the same application will exhaust the number of available sockets in the environment as stated in the article.

The recommended usage is to declare HttpClient as a static scope as shown:

static readonly HttpClient client = new HttpClient();

The static scope is preferred instead of using a scoping block:

using (HttpClient client = new HttpClient())
{
  …
}

I will look at two uses of IHttpClientFactory that use dependency injection in .NET Core to provide support for HTTP REST API calls to an external Web API.

Setting up IHTTPClientFactory in .NET Core

To setup IHttpClientFactory in .NET Core requires the following: Install the NuGet package:

Microsoft.Extensions.Http

Next, make the following declarations in Startup.cs within ConfigureServices():

using Microsoft.Extensions.Http;

public void ConfigureServices(IServiceCollection services)
{
	...
services.AddHttpClient();
	...
}

Within custom service classes the declaration uses constructor dependency injection:

private readonly IHttpClientFactory _clientFactory;
ApplicationDbContext _db;
ILogger _logger;

public LoanService(ApplicationDbContext db, 
       IHttpClientFactory httpClientFactory,
       ILogger<LoanService> logger)
{
       _db = db;
       _logger = logger;
       _clientFactory = httpClientFactory;
}

A reference to the class below is needed to expose the IHttpClientFactory provider:

using System.Net.Http;

In the next section, I will show how to use the injected IHTTPClientFactory to make the API request from within our service class.

Executing a Web API HTTP GET call from a Web Client

The first case is to retrieve a single record from a Web API.

The result of our call to the following URI:

http://localhost/BookLoan.Catalog.API/api/Book/Details/1

through a browser gives JSON data response:

{
"status":null,
"dateLoaned":"0001-01-01T00:00:00",
"dateDue":"0001-01-01T00:00:00",
"dateReturn":"0001-01-01T00:00:00",
"borrower":null,
"onShelf":false,
"dateLoanedFormattedString":"N/A",
"dateDueFormattedString":"N/A",
"dateReturnFormattedString":"N/A",
"id":1,"title":
"The Lord of the Rings",
"author":"J. R. R. Tolkien",
"yearPublished":1954,
"genre":"fantasy",
"edition":"0",
"isbn":null,
"location":"sydney",
"dateCreated":"2019-11-05T00:00:00",
"dateUpdated":"2019-11-05T00:00:00"
}

The following code shown below is from the external Web API HTTP GET controller method:

[HttpGet("api/[controller]/Details/{id}")]
public async Task<ActionResult> Details(int id)
{
   if (id == 0)
   {
      return NotFound(new { id });
   }
   try
   {
      BookStatusViewModel bvm = new BookStatusViewModel();
      var book = await _db.Books.Where(a => 
         a.ID == id).SingleOrDefaultAsync();
                	
      if (book != null)
      {
         bvm.ID = book.ID;
         bvm.Title = book.Title;
         bvm.Author = book.Author;
         bvm.Genre = book.Genre;
	…
      }
      return Ok(bvm);
    }
    catch (Exception ex)
    {
       return BadRequest( new { ex.Message });
    }
}

The following code shown below is the HTTP client call to the above Web API method:

using Newtonsoft.Json;
using System.Net.Http;

…
…

string id = “1”;
var request = new HttpRequestMessage(
   HttpMethod.Get,
                          
   "http://localhost/BookLoan.Catalog.API/api/Book/Details/" +  
      id.ToString());

var client = _clientFactory.CreateClient();
var response = await client.GetStringAsync(request.RequestUri);

BookStatusViewModel bookView = new BookStatusViewModel();

if (response.Length > 0) 
{
   try
   {
      bookView =  
         JsonConvert.DeserializeObject<BookStatusViewModel>  
         (response);
   }
   catch (Exception ex)
   {
      ... 
   }
}

Notes:

1. To use the JsonConvert class we require the Newtonsoft.Json namespace. 2. To use the IHttpClientFactory factory and associated methods we require the System.Net.Http namespace.

Before we could transfer response data from the HTTP request into our local data, we had to obtain a HTTP client connection from the HTTP client factory:

var client = _clientFactory.CreateClient();

Deserializing JSON Responses into a Useable .NET Type

When we make calls to an external HTTP GET web API method, the response we receive is in the JSON format. We make use of the following method:

JsonConvert.DeserializeObject<T>(string)

To convert a JSON response into a .NET type.

Next, we will look at retrieving multiple records from a Web API.

The result of our call to the following URI:

http://localhost/BookLoan.Catalog.API/api/Book/List

through a browser gives JSON data array response:

[
{"id":1,
"title":"The Lord of the Rings",
"author":"J. R. R. Tolkien", 
"yearPublished":1954,
"genre":"fantasy", 
"edition":"0",
"isbn":"",
"location":"sydney"
},
{"id":2,
"title": "The Alchemist (O Alquimista)",
"author":"Paulo Coelho", 
"yearPublished":1988, "genre":"fantasy", "edition":"0",
"isbn":"",
"location":"sydney"
},
…
…
{"id":7,
"title":"And Then There Were None",
"author":"Agatha Christie", "yearPublished":1939,
"genre":"mystery", 
"edition":"0",
"isbn":"",
"location":"sydney"
}
]

The following code shown below is from the external Web API HTTP GET controller method:

[HttpGet("api/[controller]/List")]
public async Task<List<BookViewModel>> List()
{
   try
   {
      List<BookViewModel> bvm = new List<BookViewModel>();
      var books = await _db.Books.ToListAsync();
      return books;
   }
   catch (Exception ex)
   {
     throw;
   }
}

The following code shown below is the HTTP client call to the above Web API:

using Newtonsoft.Json;
using System.Net.Http;
…
…

BookViewModel book = new BookViewModel();

var request = new HttpRequestMessage(HttpMethod.Get,
"http://localhost/BookLoan.Catalog.API/api/Book/List");

var client = _clientFactory.CreateClient();

var response = await client.SendAsync(request);

List<BookViewModel> bookViews = new List<BookViewModel>();

if (response.IsSuccessStatusCode)
{
   string respContent = 
      response.Content.ReadAsStringAsync().Result;

   bookViews = JsonConvert.DeserializeObject
      <List<BookViewModel>>(respContent.ToString());
}
else
{
   ...
}

Note: The only difference here is that we deserialize with List<T>.

Using the JArray Class to Parse JSON Array Responses

Another variation of the above call is to use JArray class to parse out the JSON array structure:

using Newtonsoft.Json;
using System.Net.Http;
using Newtonsoft.Json.Linq;
…
…

BookViewModel book = new BookViewModel();

var request = new HttpRequestMessage(HttpMethod.Get,
                        "http://localhost/BookLoan.Catalog.API/api/Book/List");

var client = _clientFactory.CreateClient();

var response = await client.SendAsync(request);

List<BookViewModel> bookViews = new List<BookViewModel>();

if (response.IsSuccessStatusCode)
{
   string respContent = 
     response.Content.ReadAsStringAsync().Result;

   JArray books = JArray.Parse(respContent) as JArray;
   foreach (var item in books)
   {
      BookViewModel bitem = JsonConvert.DeserializeObject
        <BookViewModel>(item.ToString());
        bookViews.Add(bitem);
   }
}
else
{
   ...
}

Note: To use the JArray class we require the Newtonsoft.Json.Linq namespace.

The above overview has shown us how to execute HTTP GET calls from one .NET web client or .NET web API to another web API.

In addition, we have seen how to parse the response into a usable .NET type, where we can retrieve the list of data.

That’s all for this post.

I hope you have found this useful and informative.

Social media & sharing icons powered by UltimatelySocial