当前我正在构建一个解决方案,它基本上是多个外部 REST 服务之间的集成。由于解决方案具有多个外部服务,因此我想在需要时使用 .NET ClientFactory 创建 HttpClient。不幸的是,从 ClientFactory 创建的 HttpClient 实例始终会导致未经授权。这有点奇怪,因为直接创建一个新的 HttpClient 就会成功。我红色了有关 HttpClientFactory 的 Microsoft 文档,以确定差异和可能的用例。
我通过提取整个对象来比较两个不同的 HttpClient 实例,并进行融合比较,没有任何差异。另外,通过邮递员,对服务的调用也会成功。
提取并创建 HttpClient 实例(失败)的代码如下所示:
程序.cs
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
services.AddHttpClient("ClientName", client =>
{
client.BaseAddress = new Uri("https://my.domain.com");
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("username:password"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
});
TicketRepository.cs
public class TicketsRepository(
IHttpClientFactory _httpClientFactory) : ITicketsRepository
{
private readonly HttpClient _httpClient = _httpClientFactory.CreateClient("ClientName");
public async Task<Tickets> GetTicketByNumber(
int ticket,
CancellationToken cancellationToken)
{
HttpResponseMessage response = await _httpClient.GetAsync(
$"{Constants.TicketsUrlTemplate}/{ticket}",
cancellationToken);
if (!response.IsSuccessStatusCode)
{
_logger.LogError("Could not successfully retrieve data. Invalid response data is: {response}", response.ToString());
throw new HttpRequestException($"Server did not respond with an http response status 200: {response}");
}
return new Tickets();
}
}
在每个方法调用上创建 HttpClient 实例并成功提取数据的代码如下所示:
public async Task GetTicketByNumber(
int ticket,
CancellationToken cancellationToken)
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://my.domain.com");
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("username:password"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
HttpResponseMessage response = await client.GetAsync($"/urlTemplate/{ticket}/", cancellationToken);
string responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine("Response Content: " + responseContent);
}
catch (HttpRequestException e)
{
Console.WriteLine("Request error: " + e.Message);
}
}
}
我与具有相同标头、版本、主机等的响应对象和 HttpClient 对象进行了空白比较...我一定忽略了一些愚蠢的事情,因为 ClientFactory 适用于启动时配置的其他 Rest 服务。很难监视服务器端的东西,因为我没有对该 REST 服务的任何物理访问权限,也无法访问它的源代码。希望有人能指出我正确的方向。
随着我的调查在周末继续进行(之前出现的问题),我不得不承认我应该进一步研究。当我比较新创建的 HttpClient 和 ClientFactory 中的 HttpResponseMessages 时,我注意到后续调用返回的标头
WwwAuthenticate
为空,而 HttpClientFactory 中的标头具有以下标头值 Basic realm="api"
。因此,我进行了更多搜索并找到了这个解决方案,使用 HttpClientFactory 对每个请求进行身份验证,它向我指出了如何将 HttpClientHandler 与 IHttpClientFactory 一起使用。事实证明,ClientFactory 上的以下附加配置成功执行,因为返回的 WwwAuthenticate 标头必须为空:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
services.AddHttpClient("Server", client =>
{
client.BaseAddress = new Uri("https://my.domain.com");
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("username:password"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}).ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseDefaultCredentials = true,
Credentials = new NetworkCredential("", ""),
};
});
虽然这有效,但我也必须承认,我需要在这样的服务器上调查更多有关基本身份验证的信息,并且请求仍然必须与直接创建的 HttpClient 实例不同,因为我想了解更多信息。 Soo,不幸的是对我来说还不是一个真正正确的答案,但对于 POC 来说这是一个可行的解决方案。对于使用附加
ConfigurePrimaryHttpMessageHandler
(包括默认凭据)的更多技术部门的任何评论,我们表示赞赏。