dpant
dpant

Reputation: 2044

How HttpClient really works (aka What is really wrong with HttpClient)?

Two years ago, in an ASP.NET Core 8 MVC web app, I used HttpClient to make HTTP requests to an API, like this:

HttpClientHandler httpClientHandler = new HttpClientHandler()
{
    Credentials = new NetworkCredential(configuration[Id], configuration[Pwd])
};

using var client = new HttpClient(httpClientHandler);
client.DefaultRequestHeaders.Add("Accept", "application/xml");

var result = client.GetAsync($"{configuration[Host]}/api/example").Result;

I know this is not the recommended way to use HttpClient but I needed to set the basic authentication credentials and the API's base address (host) dynamically so I couldn't register a static HttpClient with the DI container of the app; doing so seemed to be extremely complicated because the Host, Id, and Pwd settings exist both in appsettings.json and appsettings.Development.json and are user-profile based, for example:

"API": {
  "Debug": {
      "Host": "...",
      "Id": "...",
      "Pwd": "..."
  },
  "Production": {
    "Host": "...",
    "PartnerId": "...",
    "Pwd": "..."
  }
}

where "Debug" and "Production" are two profiles and the actual profile is based on the logged-in user and therefore it's determined at runtime.

For two years the above code worked fine until yesterday when all requests started to fail with response:

<?xml version="1.0" encoding="UTF-8"?>
<error>
  <message context="ForbiddenException">Forbidden resource</message>
</error>

Of course, at first, I suspected that something has changed on the API side. Nevertheless, sending the same requests from Postman worked fine! Replacing HttpClient with HttpWebRequest worked fine, too!

Anyone knows how can I discover what is suddenly wrong with the HttpClient requests?

Upvotes: -2

Views: 111

Answers (1)

dpant
dpant

Reputation: 2044

After taking all comments into serious consideration here's what I came up with:

Program.cs

services.AddHttpClient<MyService>();

MyService.cs

private readonly HttpClient _httpClient;

public MyService(HttpClient httpClient)
{
  _httpClient = httpClient;
  _httpClient.BaseAddress = new Uri(baseAddress);
  _httpClient.DefaultRequestHeaders.Add("Accept", "application/xml");        
  _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
}

all requests are made by setting up a HttpRequestMessage and calling SendAsync:

var request = new HttpRequestMessage
{
    Method = method,
    RequestUri = new Uri(endpoint, UriKind.Relative),
    Content = !string.IsNullOrEmpty(body) ? new StringContent(body, Encoding.UTF8, "application/xml") : null,
};            
            
var response = await _httpClient.SendAsync(request);

Goals achieved:

  • Successful requests to the API
  • Use of Basic authentication
  • Single, static, typed HttpClient injected as per the guidelines
  • async/await instead of .Results

Thanks to all commentators. @PanagiotisKanavos, most probably you're right. It used to work. The response error suggests that something was wrong with the authentication. Maybe something was changed on the API side. Whatever it was it affected HttpClient but not HttpWebRequest. The latter is something I would very much like to know exactly why.

Upvotes: 1

Related Questions