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

Using IHttpClientFactory for Web API HTTP POST REST Calls

Welcome to today’s post.

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

In a previous post I showed how to execute HTTP GET calls from within a .NET Core application. In this post I will be focusing on the execution of HTTP POST calls from within a .NET Core web application (a web client or an 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 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();

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 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 the next section I will show how to use the above IHTTPClientFactory provider to make HTTP POST calls to and receive a response from an external Web API.

Executing a Web API HTTP POST call from a Web Client

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": "admin@bookloan.com",
"password": "P@ss123"
}

Returns a response:

{
    "username": "admin@bookloan.com",
    "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.

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

In addition, we have seen how to post a JSON body to the web API, then parse the response into a usable .NET type, where we can retrieve the structured result.

That’s all for this post.

I hope you have found this useful and informative.

Social media & sharing icons powered by UltimatelySocial