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?
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?
1 Answer
Reset to default 1After 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.
HttpClient
works fine, probably the server got unhappy with what you were sending it. Almost certainly a missing/incorrect header. Use a transparent proxy such as Fiddler to compare working and non-working response to see what the difference is. Also don't ever use.Result
usevar result = await client.GetAsync($"{configuration[Host]}/api/example");
instead – Charlieface Commented Feb 5 at 12:28AddHttpClient
as several of the overloads passIServiceProvider
as a parameter. – Panagiotis Kanavos Commented Feb 5 at 14:26NetworkCredential
is used for multiple authentication types, including NTLM, Basic, Kerberos. The NetworkCredentials documentation adds the credential to a CredentialCache with an explicit authentication type. Perhaps the code was using NTLM until now and the server disabled it? – Panagiotis Kanavos Commented Feb 5 at 14:47