#c# #asp.net-core #dotnet-httpclient
#c# #asp.net-ядро #dotnet-httpclient
Вопрос:
У меня есть Asp.Net Core WebApi
. Я делаю Http-запросы в соответствии с шаблоном HttpClientFactory. Вот мой пример кода:
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);
...
}
}
Я хочу реализовать отправку запросов через динамический прокси. По сути, это означает, что мне может потребоваться менять прокси с каждым запросом. На данный момент я нахожу 2 варианта, ни один из которых мне не кажется хорошим:
1. Иметь статический прокси, подобный этому:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient<IMyInterface, MyService>().ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true
};
});
...
}
Но при таком подходе у меня может быть только один прокси для каждой службы.
2. Утилизируйте HttpClient
с каждым запросом:
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);
...
}
Но таким образом я нарушаю шаблон HttpClientFactory, и это может вызвать проблемы с производительностью приложения, как указано в следующей статье
Есть ли третий способ, при котором я мог бы динамически изменять прокси без повторного создания HttpClient
?
Комментарии:
1. Согласно этому сообщению: learn.microsoft.com/en-us/dotnet/standard/… Каждый раз, когда вы получаете объект HttpClient из IHttpClientFactory, возвращается новый экземпляр. Но каждый HttpClient использует HttpMessageHandler, который объединяется в пул и повторно используется IHttpClientFactory для уменьшения потребления ресурсов, пока срок службы HttpMessageHandler не истек. Таким образом, он ограничен, но с привилегиями.
Ответ №1:
Невозможно изменить какое-либо из свойств HttpClientHandler
или назначить новую версию HttpClientHandler
существующей HttpClient
после ее создания. Таким образом, тогда невозможно иметь динамический прокси для конкретного HttpClient
: вы можете указать только один прокси.
Правильный способ добиться этого — вместо этого использовать именованные клиенты и определять клиента для каждой конечной точки прокси. Затем вам нужно ввести IHttpClientFactory
и выбрать один из используемых прокси-серверов, запрашивая именованный клиент, который это реализует.
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
};
});
...
Затем:
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. Что делать, если вы не знаете URL прокси заранее?
2. Тогда вам действительно не повезло? В каком сценарии вы бы не знали что-то подобное заранее? Возможно, вы не знаете, какой прокси использовать до выполнения, но вы, по крайней мере, знаете возможности. Вы настраиваете именованные клиенты для каждой из возможностей, а затем можете легко переключать их во время выполнения.
Ответ №2:
Я могу сделать это, унаследовав от 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);
}
}
Затем я регистрирую свой ProxyHttpHandler
и ProxyOptions
в IoC:
public IForksCoreConfigurationBuilder ConfigureProxy(Action<ProxyOptions> options)
{
Services.AddOptions<ProxyOptions>().Configure(options);
Services.AddTransient<ProxyHttpHandler>();
Services.AddHttpClient<IService, MyService>()
.ConfigurePrimaryHttpMessageHandler<ProxyHttpHandler>();
return this;
}
Комментарии:
1. Во втором запросе вы не можете снова установить прокси, есть исключение: этот экземпляр уже запустил один или несколько запросов