如何使用IHttpClientFactory

时间:2019-05-19 02:45:49

标签: c# asp.net-core appsettings httpclientfactory

我正在使用IHttpClientFactory发送请求并使用Net Core 2.2从两个外部API接收HTTP响应。

我正在寻找一种很好的策略,以使用存储在appsettings.json中的刷新令牌来获取新的访问令牌。当前请求返回403或401错误时,需要请求新的访问令牌。获得新的访问和刷新令牌后,需要使用新值更新appsettings.json以便在后续请求中使用。

我正在使用两个客户端向两个不同的API发送请求,但只有其中一个使用令牌身份验证机制。

我实现了一些简单的可行方法,但是我正在寻找一种更优雅的解决方案,该解决方案可以在当前令牌到期时动态更新标头:

我已经在Startup.ConfigureServices方法中注册了IHttpClientFactory,如下所示:

services.AddHttpClient();

注册后,我将以两种不同的方法使用它来调用两种不同的API,第一种方法是:

   public async Task<AirCallRequest> GetInformationAsync(AirCallModel model)
    {
        try
        {


            CandidateResults modelCandidateResult = null;

            var request = new HttpRequestMessage(HttpMethod.Get,
            "https://*******/v2/*****");
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _appSettings.Value.Token);


            var clientJAAPI = _httpClientFactory.CreateClient();
            var responseclientJAAPI = await clientJAAPI.SendAsync(request);


            if (responseclientJAAPI.IsSuccessStatusCode)
            {
                modelCandidateResult = await responseclientJAAPI.Content
                   .ReadAsAsync<CandidateResults>();

                ....
            }


            if ((responseclientJAAPI .StatusCode.ToString() == "Unauthorized")
            {                    

                await RefreshAccessToken();

               //Calls recursively this method again
                return await GetInformationAsync(model);

            }

            return null;
        }
        catch (Exception e)
        {
            return null;

        }

    }

刷新令牌方法如下所示:

private async Task RefreshAccessToken()
    {


        var valuesRequest = new List<KeyValuePair<string, string>>();
        valuesRequest.Add(new KeyValuePair<string, string>("client_id", "*****"));
        valuesRequest.Add(new KeyValuePair<string, string>("client_secret","****"));
        valuesRequest.Add(new KeyValuePair<string, string>("grant_type", "refresh_token"));
        valuesRequest.Add(new KeyValuePair<string, string>("refresh_token", "*****"));


        RefreshTokenResponse refreshTokenResponse = null;

        var request = new HttpRequestMessage(HttpMethod.Post,
        "https://*****/connect/token");

        request.Content = new FormUrlEncodedContent(valuesRequest);

        var clientJAAPI = _httpClientFactory.CreateClient();
        var responseclientJAAPI = await clientJAAPI.SendAsync(request);

        if (responseclientJAAPI.IsSuccessStatusCode)
        {
            refreshTokenResponse = await responseclientJAAPI.Content.ReadAsAsync<RefreshTokenResponse>();

            //this updates the POCO object representing the configuration but not the appsettings.json :
            _appSettings.Value.Token = refreshTokenResponse.access_token;

        }

    }

请注意,我正在更新表示配置的POCO对象,而不是appsettings.json,因此新值存储在内存中。我想为后续请求更新appsettings.json。

如果提出的解决方案需要在Startup.ConfigureService中定义Httpclient的主要设置,则它需要允许创建HttpClien的不同实例,因为其中一个HttpClient实例(在另一种方法中使用以调用第二个API) )不需要令牌即可发送请求。

1 个答案:

答案 0 :(得分:1)

好像您需要DelegatingHandler。您可以用两个词“拦截”您的http请求并添加Authorization标头,然后尝试执行它,如果令牌无效,请刷新令牌并重试一次。像这样:

public class AuthenticationDelegatingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = await GetToken();
        request.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, token.AccessToken);
        var response = await base.SendAsync(request, cancellationToken);

        if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
        {
            token = await RefreshToken();
            request.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, token.AccessToken);
            response = await base.SendAsync(request, cancellationToken);
        }

        return response;
    }
}

您可以像这样在Startup.cs中注册此委托处理程序:

services.AddTransient<AuthenticationDelegatingHandler>();
services.AddHttpClient("MySecuredClient", client =>
    {
        client.BaseAddress = new Uri("https://baseUrl.com/");
    })
    .AddHttpMessageHandler<AuthenticationDelegatingHandler>();

并像这样使用:

var securedClient = _httpClientFactory.CreateClient("MySecuredClient");
securedClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "v2/relativeUrl"));

关于将刷新令牌存储在appsetting.json中。我不认为这是个好主意,因为更新代币没有到期时间。如果您可以首次使用凭据获取新令牌,请使用它,然后将刷新令牌存储在内存中以进行进一步刷新。

Here,您可以看到我如何管理客户端凭据令牌刷新并尝试使其适用于您的方案。