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

Using HttpClientFactory for Web API HTTP GET REST Calls

Welcome to today’s post.

I will be discussing the approaches I have taken to use HTTP REST calls between Web API services.

I will be focusing on utilizing the Microsoft http extensions package to provide 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.

First, 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;

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();

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>.

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.

That’s all for this post.

I hope you have found this useful and informative.

Social media & sharing icons powered by UltimatelySocial