Having dynamic proxy with HttpClientFactory implementation

时间:2019-04-08 12:59:47

标签: c# asp.net-core dotnet-httpclient

I have Asp.Net Core WebApi. I am making Http requests according to HttpClientFactory pattern. Here is my sample code:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddHttpClient<IMyInterface, MyService>();
    ...
}

public class MyService: IMyInterface
{
    private readonly HttpClient _client;

    public MyService(HttpClient client)
    {
        _client = client;
    }

    public async Task CallHttpEndpoint()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
        var response = await _client.SendAsync(request);
        ...
    }

}

I want to implement sending requests through dynamic proxy. This basically means that I might need to change proxy with each request. As for now I find out 2 approuces, non of which seems good to me:

1.Have a static proxy like this:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddHttpClient<IMyInterface, MyService>().ConfigurePrimaryHttpMessageHandler(() =>
        {
            return new HttpClientHandler
            {
                Proxy = new WebProxy("http://127.0.0.1:8888"),
                UseProxy = true
            };
        });
    ...
}

But I can only have single proxy per service in this approach.

2.Dispose HttpClient with each request:

    HttpClientHandler handler = new HttpClientHandler()
    {
        Proxy = new WebProxy("http://127.0.0.1:8888"),
        UseProxy = true,
    };

    using(var client = new HttpClient(handler))
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
        var response = await client.SendAsync(request);
        ...
    }

But in this way I violate HttpClientFactory pattern and it might cause issues to application performance as stated in following article

Is there a third way where I could change proxy dinamically without re-creating HttpClient?

2 个答案:

答案 0 :(得分:3)

There is no way to change the any of the properties of HttpClientHandler or to assign a new version of HttpClientHandler to an existing HttpClient after it is instantiated. As such, it is then impossible to have a dynamic proxy for a particular HttpClient: you can only specify one proxy.

The correct way to achieve this is to use named clients, instead, and define a client for each proxy endpoint. Then, you'll need to inject IHttpClientFactory and pick one of the proxies to use, requesting the named client that implements that.

services.AddHttpClient("MyServiceProxy1").ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler
    {
        Proxy = new WebProxy("http://127.0.0.1:8888"),
        UseProxy = true
    };
});

services.AddHttpClient("MyServiceProxy2").ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler
    {
        Proxy = new WebProxy("http://127.0.0.1:8889"),
        UseProxy = true
    };
});

...

Then:

public class MyService : IMyInterface
{
    private readonly HttpClient _client;

    public MyService(IHttpClientFactory httpClientFactory)
    {
        _client = httpClientFactory.CreateClient("MyServiceProxy1");
    }

    public async Task CallHttpEndpoint()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
        var response = await _client.SendAsync(request);
        ...
    }
}

答案 1 :(得分:0)

我可以通过从HttpClientHandler继承来做到这一点:

public class ProxyHttpHandler : HttpClientHandler
{
    private int currentProxyIndex = 0;

private ProxyOptions proxyOptions;

public ProxyHttpHandler(IOptions<ProxyOptions> options)
{
    proxyOptions = options != null ? options.Value : throw new ArgumentNullException(nameof(options));
    UseProxy = true;
}

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    var proxy = proxyOptions.Proxies[currentProxyIndex];
    var proxyResolver = new WebProxy(proxy.Host, proxy.Port)
    {
        Credentials = proxy.Credentials
    };

    Proxy = proxyResolver;

    currentProxyIndex++;
    if(currentProxyIndex >= proxyOptions.Proxies.Count)
        currentProxyIndex = 0;

    return base.SendAsync(request, cancellationToken);
}

}

然后我在IoC中注册ProxyHttpHandlerProxyOptions

public IForksCoreConfigurationBuilder ConfigureProxy(Action<ProxyOptions> options)
{
    Services.AddOptions<ProxyOptions>().Configure(options);
    Services.AddTransient<ProxyHttpHandler>();
    Services.AddHttpClient<IService, MyService>()
            .ConfigurePrimaryHttpMessageHandler<ProxyHttpHandler>();

    return this;
}