从依赖注入配置进行依赖调用

Ant*_*ony 1 c# dependency-injection dotnet-httpclient .net-core .net-5

我的需要是注入 HttpClient 并立即可供使用。但需要注意的是 HttpClient 需要设置Authorization标头,为此我需要再进行一次调用以获取令牌。我设法在启动的 RegisterServices 中完成所有这些配置,但我怀疑这是否是一个好主意。

services.AddHttpClient("OidcClient", (isp, client) =>
{
    var options = isp.GetRequiredService<IOptions<MyConfig>>().Value;
    client.BaseAddress = new Uri(options.OidcUrl);
});

services.AddHttpClient("MyClient", (isp, client) =>
{
    var options = isp.GetRequiredService<IOptions<MyConfig>>().Value;
    var oidcClient = isp.GetRequiredService<IHttpClientFactory>().CreateClient("OidcClient");
    var data = new Dictionary<string, string>
    {
        {"client_id", options.ClientId},
        {"client_secret", options.ClientSecret}
    };

    var request = new HttpRequestMessage(HttpMethod.Post, "/connect/token") { Content = new FormUrlEncodedContent(data) };
    var response = oidcClient.SendAsync(request).Result;

    var token = response.Content.ReadFromJsonAsync<TokenResponse>().Result;

    client.BaseAddress = new Uri(options.Url);
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);
});
Run Code Online (Sandbox Code Playgroud)

然后它被正确地注入到我的代码中,我可以使用带有授权标头的客户端。

所以,我的担忧是:

  • 在依赖配置中进行 HTTP 调用是否正常?
  • 还没有找到比.Result异步调用更好的东西,还有其他选择吗?

主要问题是:在 DI 配置中进行依赖项调用是一个“好主意”吗?

Cod*_*ter 5

在依赖项配置中进行 HTTP 调用是否正常[/一个“好主意”]?

不,这与启动和运行依赖项所需的最低要求相去甚远,它包含属于 API 客户端类的特定于实现的行为。

您也不知道应用程序将运行多长时间以及令牌将有效多长时间,因此期望在其构造函数中获取有效且准备就绪的 HttpClient 的代码可以根据需要保留它。

将 HttpClient 包装在第三方或您自己的包装器中,其中包括 OAuth 流程。当您的代码想要首次调用 API 时,或者当令牌过期时,或者当您使用现有令牌收到 401 时,请进行身份验证调用。

正如 @TheGeneral 所评论的,这种包装器的一种方法是使用 a,如本博客文章DelegatingHandler中所解释的。概括:

设置:

// The DelegatingHandler has to be registered as a Transient Service
services.AddTransient<ProtectedApiBearerTokenHandler>();

// Register our ProtectedApi client with a DelegatingHandler
// that knows how to obtain an access_token
services.AddHttpClient<IProtectedApiClient, ProtectedApiClient>(client =>
{
    client.BaseAddress = new Uri("http://localhost:5002");
    client.DefaultRequestHeaders.Add("Accept", "application/json");
}).AddHttpMessageHandler<ProtectedApiBearerTokenHandler>();
Run Code Online (Sandbox Code Playgroud)

处理程序:

public class ProtectedApiBearerTokenHandler : DelegatingHandler
{
    private readonly IIdentityServerClient _identityServerClient;

    public ProtectedApiBearerTokenHandler(
        IIdentityServerClient identityServerClient)
    {
        _identityServerClient = identityServerClient 
            ?? throw new ArgumentNullException(nameof(identityServerClient));
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        // request the access token
        var accessToken = await _identityServerClient.RequestClientCredentialsTokenAsync();

        // set the bearer token to the outgoing request
        request.SetBearerToken(accessToken);

        // Proceed calling the inner handler, that will actually send the request
        // to our protected api
        return await base.SendAsync(request, cancellationToken);
    }
}
Run Code Online (Sandbox Code Playgroud)