简体   繁体   English

在服务之间共享 HttpClient

[英]Share HttpClient between services

I am working on a Blazor project, and to make the question I have easier to understand, we can say that I am using two different services that handles the Authentication part.我正在研究一个 Blazor 项目,为了让我更容易理解这个问题,我们可以说我正在使用两种不同的服务来处理身份验证部分。 Those are registered in the configureservices startup method together with a named httpclient.它们与命名的 httpclient 一起注册在 configureservices 启动方法中。

services.AddHttpClient("XBOWServicesApi", c =>
{
c.BaseAddress = new Uri(XBOWServicesApi);
});

services.AddSingleton<IService1, Service1>();
services.AddSingleton<IService2, Service2>();

Service 1: Wraps all functionality available in a REST Api.服务 1:包含 REST Api 中可用的所有功能。 It uses an http client which is set in the constructor via an instanciated httpclientfactory.它使用 http 客户端,该客户端通过实例化的 httpclientfactory 在构造函数中设置。 This needs to be set with a baseurl and an Auth-header to work.这需要使用 baseurl 和 Auth-header 进行设置才能工作。

public Service1(IHttpClientFactory clientFactory)
{
this.httpClient = clientFactory.CreateClient("XBOWServicesApi");
}

Service 2: Handles the login/logout functionality using a custom AuthenticationStateProvider.服务 2:使用自定义 AuthenticationStateProvider 处理登录/注销功能。 It has its own httpclient, so that I can set the Auth Header for the http client.它有自己的httpclient,所以我可以为http客户端设置Auth Header。 The constructor works in the same way as for Service 1.构造函数的工作方式与服务 1 相同。

public Service2(IHttpClientFactory clientFactory)
{
this.httpClient = clientFactory.CreateClient("XBOWServicesApi");
}

The reason for this build up is of course that I like to share the same http client, so when it is set in the login/logout methods, service 1 will have the correct auth header when communicating with the api.这种建立的原因当然是我喜欢共享同一个 http 客户端,所以当它在登录/注销方法中设置时,服务 1 在与 Z8A5DA52ED1205447A8AAZE 通信时将具有正确的身份验证 header。

However, the client factory provides a new instance everytime, so this will never work.但是,客户端工厂每次都提供一个新实例,所以这永远不会起作用。

Any ideas how to handle this?任何想法如何处理这个?

/Henrik /亨里克

When I read through the Microsoft IHttpClientFactory docs :当我阅读Microsoft IHttpClientFactory 文档时:

Each time you get an HttpClient object from the IHttpClientFactory, a new instance is returned.每次从 IHttpClientFactory 获取 HttpClient object 时,都会返回一个新实例。 But each HttpClient uses an HttpMessageHandler that's pooled and reused by the IHttpClientFactory to reduce resource consumption, as long as the HttpMessageHandler's lifetime hasn't expired.但是每个 HttpClient 使用一个由 IHttpClientFactory 汇集和重用的 HttpMessageHandler 来减少资源消耗,只要 HttpMessageHandler 的生命周期没有过期。

Does that answer your question?这是否回答你的问题?

You can use named client :您可以使用named client

services.AddHttpClient("github", c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    // Github API versioning
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    // Github requires a user-agent
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

Afterwards, just call CreateClient method with corresponding name parameter.之后,只需调用带有相应name参数的CreateClient方法。

var client = _clientFactory.CreateClient("github");

Each time CreateClient is called:每次调用CreateClient时:

  • A new instance of HttpClient is created.创建了一个新的 HttpClient 实例。
  • The configuration action is called.调用配置操作。

You can find more details in Microsoft documentation here .您可以在此处的 Microsoft 文档中找到更多详细信息。

You can share scoped services between transient HttpClients by using HttpMessageHandlers.您可以使用 HttpMessageHandlers 在瞬态 HttpClients 之间共享作用域服务。

IHttpClient.CreateClient returns a new instance every time, but you can register a HttpMessageHandler as shown below: IHttpClient.CreateClient每次都会返回一个新的实例,但是你可以注册一个HttpMessageHandler如下所示:

services.AddScoped<HandlerData>();
services.AddTransient<HeaderHandler>();
services.AddHttpClient("XBOWServicesApi", c =>
{
c.BaseAddress = new Uri(XBOWServicesApi);
}).AddHttpMessageHandler<HeaderHandler>();

HeaderHandler Class: HeaderHandler Class:

public class HeaderHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor httpContextAccessor;
    public HeaderHandler(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken )
    {
        var Data= this.httpContextAccessor.HttpContext.RequestServices.GetRequiredService<HandlerData>();
        request.Headers.Add(Data.HeaderName, Data.HeaderValue);
        return base.SendAsync(request, cancellationToken);
    }
}

HandlerData Class:处理程序数据 Class:

public class HandlerData
{
    public string HeaderName { get; set; }
    public string HeaderValue { get; set; }
}

ServicesCode:服务代码:

public Service1(IHttpClientFactory clientFactory, HandlerData data)
{
    data.HeaderName = "Header1";
    data.HeaderValue = "Value";
    this.httpClient = clientFactory.CreateClient("XBOWServicesApi");
}
public Service2(IHttpClientFactory clientFactory)
{
    //This will contain the same headers as Service1 as HandlerData is Scoped Service
    this.httpClient = clientFactory.CreateClient("XBOWServicesApi");
}

Alternatively, you can also use new IHttpMessageHandlerFactory if you need to create handlers that live in the same DI scope as you request:或者,如果您需要创建与您请求相同的 DI scope 的处理程序,您也可以使用新的IHttpMessageHandlerFactory

Reference: https://github.com/aspnet/HttpClientFactory/issues/166参考: https://github.com/aspnet/HttpClientFactory/issues/166

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM