使用HttpClientFactory在.NET Core 2.1中实现每个请求的身份验证。

6

我应该如何使用HttpClientFactory返回一个在调用点确定uri和凭据的HttpClient实例?

现有代码如下:

var httpClientHandler = new HttpClientHandler()
    {
        Credentials = new NetworkCredential(userName, password),
    };
HttpClient client = new HttpClient(httpClientHandler);
client.BaseAddress = new Uri(_appSetting.ServiceURI);
3个回答

14

在启动类中,你的ConfigureServices方法

services.AddHttpClient("github", c =>
{
    //c.BaseAddress = new Uri("https://api.github.com/");
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
    
}).ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler()
    {
        UseDefaultCredentials = true,
        Credentials = new NetworkCredential("", ""),
    };
});

你的控制器将会长成这个样子

private readonly IHttpClientFactory _httpClientFactory;

public DataProController(IHttpClientFactory httpClientFactory)
{
    _httpClientFactory = httpClientFactory;
}

[HttpGet]
public async Task<ActionResult> Get()
{            
    var client = _httpClientFactory.CreateClient("github");
    
    client.BaseAddress = new Uri("https://api.github.com/");

    string result = await client.GetStringAsync("/");
    return Ok(result);
}

当使用httpclientfactory时,您可能无法在运行时设置网络凭据,而需要在启动类中进行设置。您可以在此处了解有关此问题的更多信息。 https://github.com/aspnet/HttpClientFactory/issues/71


安装NuGet包Microsoft.Extensions.Http并在AddHttpClient()之后配置ConfigurePrimaryHttpMessageHandler链。 - TamusJRoyce

8
你可以按照以下方式创建一个委托身份验证处理程序:
public class AuthenticationHttpMessageHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            // Get the token or other type of credentials here
            // string scheme = ... // E.g. "Bearer", "Basic" etc.
            // string credentials = ... // E.g. formatted token, user/password etc.

            request.Headers.Authorization =
                new AuthenticationHeaderValue(scheme, credentials);

            return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
        }
    }

然后将其添加到HttpClient生成器和DI容器中:

 services
     .AddTransient<AuthenticationHttpMessageHandler>()
     .AddHttpClient("MyClient")
     .AddHttpMessageHandler<AuthenticationHttpMessageHandler>();

然后使用IHttpClientFactory创建HttpClient实例。

这种方法的核心优势是您可以清楚地分离关注点。您不会触及主处理程序,也不会手动管理客户端创建,而是利用工厂及其构建器扩展方法的全部功能。认证处理程序自然地注入到管道中,并添加授权到每个请求中。此处理程序可以通过将凭据源抽象化并使处理程序依赖于一些IAuthenticationProvider抽象来进一步增强,这将仅需要DI配置,而不需触及HttpClient配置代码。


在我看来,这似乎是最干净和可重用的解决方案。我假设 AuthenticationHttpMessageHandler 可以与其他类型的 HttpClients 共享,这是正确的吗? - kerzek
2
@kerzerk 是的。在这种情况下,您应该将处理程序添加到服务集合(services.AddTransient<AuthenticationHttpMessageHandler>())仅一次,并且需要为每个客户端指定要添加的处理程序(.AddHttpMessageHandler<AuthenticationHttpMessageHandler>())。 - sich
1
另外,如果你只对OAuth2/OpenIdConnect身份验证/授权令牌感兴趣,我建议使用著名的IdentityModel库(https://github.com/IdentityModel/IdentityModel),该库以类似的方式向HttpClient添加其消息处理程序。该库的优点在于它处理了许多关于令牌缓存和刷新等协议的棘手问题。 - sich

2
如果您使用.net依赖注入,可以将一个类的配置添加到设置代码中:

如果你使用.NET依赖注入,你可以在设置代码中添加一个类的配置:

services
    .AddTransient<DataLoader>()
    .AddHttpClient<DataLoader>().ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler()
{
    Credentials = new NetworkCredential(LoadUsernameFromConfig(), LoadPasswordFromSecureLocation())
});

现在添加 DI,将使用此凭据的 HttpClient 注入到 DataLoader 类中:
    public class DataLoader
    {
        private readonly HttpClient httpClient;

        public DataLoader(HttpClient httpClient)
        {
            this.httpClient = httpClient;
        }

        public async Task LoadData(string tableName)
        {
            var json = await httpClient.GetStringAsync("https://protected.example.com/json");
            ...
        }
    }

如果没有Imran Arshad的答案,我就不可能想出这段代码:感谢!(Original Answer翻译成"最初的回答")


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接