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

Using HttpClientFactory for Web API HTTP POST 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 utilising 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();

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, in order to setup IHttpClientFactory in .NET Core requires the us to install the following NuGet package:

Microsoft.Extensions.Http

Next, make the following declarations and additions within Startup.cs:

using Microsoft.Extensions.Http;

// This method gets called by the runtime. Use this method to add services to the container.
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 library below is needed to expose the IHttpClientFactory provider:  

using System.Net.Http;  

In today’s post I will show how to use the above httpclientfactory provider to make HTTP POST calls   to and receive a response from an external Web API.

In an earlier post I showed how to test a Web API HTTP POST method using POSTMAN.

The result of our HTTP POST call to the following URI:

http://localhost/BookLoan.Identity.API/api/Users/Authenticate

with the following request body:

{
"userName": "[email protected]",
"password": "[email protected]"
}

Returns a response:

{
    "username": "[email protected]",
    "token": "eyJhbGciOiJIUzI1NiIsInR…."
}

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

[AllowAnonymous]
[HttpPost("authenticate")]
public IActionResult Authenticate([FromBody]UserViewModel userDto)
{
   try
   {
      bool isAuthenticated = _userService.Authenticate(userDto.UserName, userDto.Password);

      if (!isAuthenticated)
         return BadRequest(new { message = "Username or password is incorrect" });

         string tokenString = _tokenManager.GenerateToken(userDto.UserName);

         return Ok(new
                {
                    Username = userDto.UserName,
                    Token = tokenString
                });
   }
   catch (Exception ex)
   {
      return BadRequest(ex.Message.ToString());
   }
}

Notice that we will either have an affirmative response Ok with some Json data or a negative response BadRequest with an error message.

In the client application, we make the call to this external API method as shown:

public async Task<IActionResult> Authenticate([FromBody] UserViewModel userDto)
{
   var request = new HttpRequestMessage(HttpMethod.Post,
                     "http://localhost/BookLoan.Identity.API/api/Users/Authenticate");

   var client = _clientFactory.CreateClient();

   var content = JsonConvert.SerializeObject(userDto);

   StringContent stringContent =
      new StringContent(content, System.Text.Encoding.UTF8,
                    "application/json");

   var response = await client.PostAsync(request.RequestUri,
      stringContent);

   string token = "";

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

      AuthenticationResponseViewModel aitem = JsonConvert.DeserializeObject<AuthenticationResponseViewModel>(respContent);
      token = aitem.Token;

      ClaimsPrincipal principal = _tokenManager.GetPrincipal(token);

      _context.User = principal;

      var option = new CookieOptions();
      option.Expires = DateTime.Now.AddMinutes(30);
      if (_context.Request.Cookies["token"] == null)
         _context.Response.Cookies.Append("token", token, option);

      return new OkResult();
   }

   throw new UnauthorizedAccessException();
}

To summarize the above, we have implemented the following:

  1. Defined the URI of the HTTP POST message.
  2. Serialized the object we wish to pass in as the body.
  3. Set the media type of content as JSON.
  4. Post the content to the URI.
  5. Check the response.

If the response status is successful:

  • Read the response as a string
  • Deserialize the string to a local object.
  • Read the data from the object
  • Return ok

If the response status is unsuccessful then return an unauthorised exception.

Note: If you wish to structure the parameters into key and value pairs before serializing them into Json content, then the following Dictionary structure can be used to build the content before serialization and posting:

Dictionary<string, string> keyvalues = 
   new Dictionary<string, string>();
keyvalues.Add("userName", userDto.UserName);
keyvalues.Add("password", userDto.Password);

var content = JsonConvert.SerializeObject(keyvalues);
…

The above is also useful for when creating generic methods that take the parameter body as a Dictionary object before calling the respective HTTP API methods.

If the see an error like the one shown:

"Unsupported Media Type"

{Method: POST, RequestUri: 'http://localhost/BookLoan.Identity.API/api/Users/Authenticate', Version: 1.1, Content: System.Net.Http.FormUrlEncodedContent, Headers:
{
  Request-Id: |51421bb3-4fb3f0b882d03b01.1.
  Content-Type: application/x-www-form-urlencoded
  Content-Length: 47
}}

Then check that the body of the request has been converted to the correct media type.

In this case submitting a body of type

application/x-www-form-urlencoded 

to a Web API POST method accepting

application/json 

will give a 415 error.

That’s all for this post.

I hope you have found this useful and informative.

Social media & sharing icons powered by UltimatelySocial