最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - How HttpClient really works (aka What is really wrong with HttpClient)? - Stack Overflow

programmeradmin13浏览0评论

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?

Share Improve this question edited Feb 5 at 16:21 marc_s 755k184 gold badges1.4k silver badges1.5k bronze badges asked Feb 5 at 12:23 dpantdpant 2,03422 silver badges39 bronze badges 11
  • 6 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 use var result = await client.GetAsync($"{configuration[Host]}/api/example"); instead – Charlieface Commented Feb 5 at 12:28
  • Have a look into this answer: stackoverflow.com/a/12023307/982149 Is it for you, perhaps? – Fildor Commented Feb 5 at 12:35
  • 3 Totally understand your frustration. However I would recommend removing the superfluous diatribe at the end of your question. I think that's why you're getting downvoted. – Sal-SE Commented Feb 5 at 13:35
  • 1 Your own question proves that nothing's wrong with HttpClient - in .NET 8 (and all .NET Core versions) HttpWebRequest is nothing more than a compatibility wrapper over HttpClient. The credentials may be wrong, or there may be something wrong on the server. You can easily use settings loaded from configuration in AddHttpClient as several of the overloads pass IServiceProvider as a parameter. – Panagiotis Kanavos Commented Feb 5 at 14:26
  • 1 Are you sure that Basic Authentication was used? NetworkCredential 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
 |  Show 6 more comments

1 Answer 1

Reset to default 1

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.

发布评论

评论列表(0)

  1. 暂无评论