Resumo: Para utilizar um proxy com o HttpClient em C#, crie umWebProxy, anexe-o a umHttpClientHandler(ouSocketsHttpHandler), e passe esse manipulador para oHttpClientconstrutor. Para produção, troque os loops manuais porIHttpClientFactory, adicioneNetworkCredentialpara proxies autenticados e envolva as chamadas em tentativas de repetição com o Polly para que IPs inativos não derrubem o seu worker.
Introdução
Se já tentou fazer scraping de um site, aceder a uma API bloqueada por região ou testar a resistência de um serviço a partir de vários IPs de saída, já sabe porque estamos aqui. Este guia explica como usar um proxy com o HttpClient em C#, desde uma configuração de cinco linhas WebProxy a um conjunto de rotação que não vaza sockets.
Um proxy HttpClient é apenas um HttpClient cujo manipulador está configurado com um WebProxy, de modo que os pedidos de saída são encaminhados através de um IP intermediário em vez de irem diretamente para o destino. Essa é toda a abstração. Todo o resto — autenticação, SOCKS5, validação SSL, rotação, tentativas de repetição — é configuração em torno dessa ideia central.
Partimos do princípio de que está familiarizado com async/await e com a dotnet CLI numa versão recente do .NET. Não assumiremos que leu o código-fonte do SocketsHttpHandler. No final, terá padrões prontos a copiar e colar para proxies não autenticados, proxies autenticados, SOCKS5, rotação com IHttpClientFactory, validação TLS segura quando um proxy está no caminho e uma tabela de resolução de problemas para os erros que inevitavelmente irá encontrar em produção. Há também uma matriz de decisão no final para que possa deixar de manter o seu próprio conjunto de proxies quando já não valer a pena o esforço. Se procura uma visão mais abrangente sobre scraping, o nosso guia introdutório sobre a criação de um scraper web com C# combina bem com este.
Um modelo mental de como usar um proxy com o HttpClient em C#
Antes de qualquer código, certifique-se de que as camadas estão corretas. HttpClient é um invólucro fino. O transporte real, incluindo a resolução do proxy, reside no seu manipulador. No .NET moderno, isso é ou HttpClientHandler (a fachada compatível com versões antigas) ou SocketsHttpHandler (o motor subjacente). Ambos expõem uma Proxy propriedade do tipo IWebProxy, e a implementação incorporada é WebProxy.
O fluxo é o seguinte:
HttpClient
|
v
HttpMessageHandler (HttpClientHandler / SocketsHttpHandler)
| Proxy = IWebProxy
v
WebProxy -> proxy server -> upstream targetDesta estratificação resultam duas consequências. Primeiro, o proxy está ligado ao manipulador, não ao cliente. Não é possível alterar HttpClient.Proxy, porque não existe tal propriedade. Se quiser um proxy diferente, precisa de um handler diferente e, portanto, de um HttpClient (ou, melhor ainda, uma fábrica que os distribua por si).
Segundo, se não atribuir um manipulador, o .NET recorrerá à resolução de proxy padrão do sistema, incluindo variáveis de ambiente como HTTPS_PROXY. Isso é conveniente num portátil de programador e surpreendente num contentor, por isso voltaremos à questão de como desativar esta funcionalidade. O mesmo se aplica se um colega definir HttpClient.DefaultProxy algum lugar no código de inicialização partilhado: todos os clientes criados posteriormente herdam-no, a menos que se substitua o manipulador.
Ao longo deste artigo, tratamos qualquer proxy em funcionamento como http://host:port, com credenciais opcionais. Sempre que vir como usar um proxy com o HttpClient em C# abaixo, esse é o padrão que estamos a configurar
Configurar um projeto C# mínimo para testes de proxy
Confirme a sua cadeia de ferramentas e, em seguida, crie uma estrutura para uma aplicação de consola. Estamos a executar tudo num SDK .NET LTS recente (os exemplos foram escritos para o .NET 8 e comportam-se da mesma forma em versões posteriores à data de redação).
dotnet --version # expect 8.x or newer
mkdir httpclient-proxy && cd httpclient-proxy
dotnet new consoleAbra a pasta em qualquer editor. Vamos manter as coisas em Program.cs para maior clareza. Torne Main async para que possamos await chamadas HTTP sem .Result interrupções:
using System.Net.Http;
static async Task Main()
{
using var client = new HttpClient();
var direct = await client.GetStringAsync("https://api.ipify.org");
Console.WriteLine($"Direct IP: {direct}");
}api.ipify.org é o ponto de extremidade de eco de IP mais barato que existe. Execute dotnet run, anote o IP e guarde esta referência. Assim que ligar um proxy, esta mesma chamada deverá apresentar o IP de saída do proxy em vez do seu. Se isso não acontecer, tem um erro de configuração, não um erro de rede.
Configurar um WebProxy não autenticado com HttpClientHandler
Comece com o caso mais simples: um proxy gratuito ou local que não requer credenciais. A receita para usar um proxy com o HttpClient em C# consiste em três objetos, nesta ordem: WebProxy, HttpClientHandler, HttpClient.
using System.Net;
using System.Net.Http;
var proxy = new WebProxy("http://203.0.113.10:8080")
{
BypassProxyOnLocal = true, // skip the proxy for localhost/loopback
UseDefaultCredentials = false // do not silently send Windows creds
};
var handler = new HttpClientHandler
{
Proxy = proxy,
UseProxy = true
};
using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(15) };
var ip = await client.GetStringAsync("https://api.ipify.org");
Console.WriteLine($"Proxied IP: {ip}");Alguns detalhes que são fáceis de ignorar. O WebProxy construtor aceita um Uri ou uma string, e o esquema é importante: http:// para proxies HTTP e HTTPS que usam CONNECT e (como veremos mais adiante) socks5:// para SOCKS. Se incorporar credenciais na URL, WebProxy irá ignorá-las, por isso não se preocupe. BypassProxyOnLocal = true é uma configuração padrão útil; evita que acidentalmente encaminhe verificações de integridade através de um IP externo. UseDefaultCredentials = false impede que o Windows envie automaticamente a identidade do utilizador atual para um proxy de terceiros, o que é o tipo de falha que só se detecta quando uma análise de segurança analisa as suas capturas de pacotes.
Este é o padrão canónico. Tudo o resto neste guia são variações da mesma receita de três objetos. Se quiser uma explicação mais aprofundada sobre quais os tipos de proxy que fazem sentido para cargas de trabalho de scraping especificamente, o guia de campo sobre os melhores tipos de proxy para web scraping é uma boa leitura complementar.
Proxies de autenticação: NetworkCredential, erros 407 e PreAuthenticate
A maioria dos proxies pagos requer autenticação. A forma .NET de expressar isso é NetworkCredential, anexado ao WebProxy próprio, não ao manipulador:
var proxy = new WebProxy("http://gateway.example.com:8080")
{
Credentials = new NetworkCredential("my-user", "my-pass")
};
var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true };
using var client = new HttpClient(handler);Existem duas armadilhas que fazem tropeçar quase toda a gente na primeira vez.
Não coloque credenciais na URL. new WebProxy("http://user:pass@host:8080") irá silenciosamente ignorar a user:pass parte. O segmento userinfo é extraído da Uri mas nunca é usado como credenciais de proxy. Passe sempre um NetworkCredential.
PreAuthenticate é para o destino, não para o proxy. Quando o proxy rejeita a sua ligação, devolve o código HTTP 407 Proxy Authentication Required. O HttpClient apresenta isso como um HttpRequestException. Alternar HttpClientHandler.PreAuthenticate = true não altera este comportamento, porque esse sinalizador controla se o servidor de destino recebe um cabeçalho preventivo Authorization em pedidos subsequentes. Não tem nada a ver com o Proxy-Authorization , que o manipulador gere por conta própria assim que definir Credentials.
Se continuar a receber o erro 407 com credenciais que parecem corretas, verifique três coisas por ordem: está a enviá-las para o anfitrião certo (alguns fornecedores separam o plano de controlo do gateway), a sua palavra-passe está codificada em URL algures a montante e a sua conta continua em boa situação. O nosso artigo sobre erros comuns de estado do proxy e como identificá-los aprofunda o assunto, caso precise de um guia sobre a gama mais ampla de erros de proxy.
Escolher um protocolo: proxies HTTP, HTTPS e SOCKS5 em C#
HttpClient não se importa se o destino é HTTP ou HTTPS, mas o protocolo do proxy é importante porque altera a forma como a ligação é estabelecida.
- Proxy HTTP (
http://...): para destinos HTTP, o proxy pode ler e reescrever o pedido. Para destinos HTTPS, o cliente emite umCONNECTe tuneliza TLS de ponta a ponta através do proxy. - Proxy de terminação HTTPS: um caso especial em que o proxy apresenta o seu próprio certificado TLS ao seu cliente e abre uma ligação TLS separada a montante. É assim que algumas APIs comerciais de scraping funcionam no modo proxy. Abordamos as implicações do SSL na secção dedicada abaixo.
- Proxy SOCKS (
socks5://,socks4://,socks4a://): um túnel da camada de transporte que não compreende HTTP. Tudo o que se possa colocar num socket TCP passa por ele.
No .NET moderno, SocketsHttpHandler vem com suporte integrado para SOCKS4, SOCKS4a e SOCKS5 (adicionado com o .NET 6, de acordo com o rastreador de problemas do runtime; verifique na documentação do SocketsHttpHandler se estiver numa pré-visualização não LTS). A configuração é a mesma WebProxy , com um esquema diferente:
var socks = new WebProxy("socks5://socks.example.com:1080")
{
Credentials = new NetworkCredential("u", "p")
};
var handler = new SocketsHttpHandler { Proxy = socks, UseProxy = true };
using var client = new HttpClient(handler);Se precisar de utilizar um proxy com o HttpClient em C# contra um ponto de extremidade SOCKS, este é o padrão. O SOCKS5 com autenticação por nome de utilizador/palavra-passe é o padrão de facto para fornecedores residenciais; o SOCKS4 é, na sua maioria, obsoleto.
Rotação de proxies de forma limpa com IHttpClientFactory
A rotação de IPs é onde a maioria dos tutoriais falha discretamente. A abordagem ingênua é assim:
// DO NOT DO THIS in a real worker
foreach (var url in proxyUrls)
{
var handler = new HttpClientHandler { Proxy = new WebProxy(url) };
var client = new HttpClient(handler); // never disposed
var html = await client.GetStringAsync(target);
}Esse código causa fugas de sockets. Cada HttpClient mantém o seu manipulador ativo, e cada manipulador mantém uma reserva de ligações subjacente. Inicie alguns milhares destes num ciclo e esgotará as portas efémeras, o que no Linux se manifesta como SocketException: Address already in use e no Windows como WinHttpException .
A solução é IHttpClientFactory. Ele gere os tempos de vida dos handlers por si, recicla-os de acordo com uma programação e permite-lhe registar um cliente nomeado ou tipado por proxy. Uma pequena configuração de DI fica assim:
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
foreach (var p in proxyPool)
{
services.AddHttpClient(p.Name, c => c.Timeout = TimeSpan.FromSeconds(20))
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
Proxy = new WebProxy(p.Url) { Credentials = p.Creds },
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(2)
});
}
var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService<IHttpClientFactory>();Agora pode escolher um proxy por pedido sem deixar escapar nada:
var rng = new Random();
async Task<string> FetchAsync(string url)
{
var pick = proxyPool[rng.Next(proxyPool.Count)];
var client = factory.CreateClient(pick.Name);
return await client.GetStringAsync(url);
}O round-robin é uma alteração de uma linha: mantém um Interlocked.Increment contador e aplique o módulo a proxyPool.Count. De qualquer forma, cada pedido atinge um manipulador válido e em pool, e IHttpClientFactory rota os manipuladores subjacentes em PooledConnectionLifetime, o que evita o problema de obsolescência de DNS de longa duração que abordaremos na próxima secção. Se quiser uma visão mais abrangente dos padrões de rotação e quando usar cada um, a nossa análise aprofundada sobre proxies rotativos aborda o lado do algoritmo em detalhe. Tenha em atenção que IHttpClientFactory as especificidades da API, incluindo tempos de vida de DI e integração com o Polly, podem variar entre versões principais; verifique novamente a página do Microsoft Learn sobre IHttpClientFactory se estiver a fixar um runtime mais antigo.
SocketsHttpHandler vs HttpClientHandler para produção
No .NET moderno (Core 2.1+ e todos os dotnet new projeto atual), HttpClientHandler é principalmente um shim de compatibilidade que delega SocketsHttpHandler nos bastidores. Para a maioria das demonstrações, os dois são intercambiáveis. Para workers e scrapers de longa duração, deve optar SocketsHttpHandler diretamente porque expõe os controlos que importam:
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://proxy:8080"),
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(2), // recycle TCP/TLS
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(30),
ConnectTimeout = TimeSpan.FromSeconds(10), // fail fast on dead proxies
AutomaticDecompression = System.Net.DecompressionMethods.All
};As duas configurações que vale a pena memorizar:
PooledConnectionLifetimecontrola por quanto tempo uma conexão no pool pode permanecer ativa antes de ser fechada. A implicação para o proxy / atualização de DNS é a verdadeira razão para definir isso. UmHttpClientmanterá uma única ligação TCP para sempre por predefinição, e as alterações de DNS no upstream (muito comuns para endpoints residenciais rotativos) nunca serão detetadas. Dois minutos é uma predefinição sensata para scrapers.ConnectTimeouté um limite máximo distinto deHttpClient.Timeout. Este último abrange toda a solicitação, enquanto o primeiro abrange apenas o handshake TCP com o proxy. Definir um valor baixo (5 a 10 segundos) é a maneira mais econômica de impedir que proxies inativos monopolizem as threads de trabalho.
AutomaticDecompression não está relacionado com proxies, mas é útil o suficiente para ser mencionado aqui, uma vez que a maioria dos pontos finais de scraping comprimirá as respostas com gzip. A semântica das propriedades em torno de PooledConnectionLifetime e similares variam entre as principais versões de tempo de execução, por isso verifique a documentação se estiver a utilizar o .NET 6 ou 7.
Lidar corretamente com a validação SSL/TLS quando um proxy está no caminho
Um proxy no caminho da solicitação complica o TLS, mas a regra é simples: nunca desative a validação por padrão. DangerousAcceptAnyServerCertificateValidator existe porque a Microsoft quis deixar claro que, ao defini-lo, está a aceitar certificados falsificados. Em proxies gratuitos ou partilhados, isso é literalmente uma vulnerabilidade de tipo «man-in-the-middle» à espera de ser explorada por quem quer que seja que execute o proxy.
Há dois casos distintos que devem ser mantidos separados.
Os túneis CONNECT e SOCKS transportam os seus bytes TLS de ponta a ponta. O certificado que vê é o certificado real do site de destino. A validação deve permanecer ativada, ponto final. Se obtiver uma falha no handshake SSL aqui, o proxy está mal configurado ou o certificado upstream está genuinamente incorreto. Não ignore o problema.
Os proxies com terminação TLS (algumas APIs de scraping funcionam neste modo) completam intencionalmente o handshake por si próprios e apresentam o seu próprio certificado. Nesse caso, aceitar uma CA desconhecida faz parte do contrato, mas apenas para esse proxy específico. O padrão seguro é uma impressão digital ou um callback com CA fixada:
var expectedThumbprint = "AABBCCDDEEFF00112233445566778899AABBCCDD";
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://tls-terminating-proxy:8080"),
SslOptions = new System.Net.Security.SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (sender, cert, chain, errors) =>
{
if (cert is null) return false;
return string.Equals(cert.GetCertHashString(),
expectedThumbprint,
StringComparison.OrdinalIgnoreCase);
}
}
};Isso ainda é uma flexibilização, mas com âmbito limitado: apenas o certificado que corresponda à impressão digital fixada é aceite, e o resto do mundo ainda tem de passar pela validação normal da cadeia. Se estiver a fazer scraping em grande escala e precisar de usar um proxy com HttpClient em C# contra um gateway de terminação TLS, esta é a configuração segura para produção.
Repetidas tentativas, tempos de espera e recuo exponencial com o Polly
Os proxies falham. Os IPs residenciais ficam offline a meio da sessão, os intervalos de endereços dos centros de dados são redirecionados para o vazio, os destinos a montante limitam a sua taxa de acesso durante dez minutos e depois voltam. A resposta correta é tentar novamente com recuo, não fazer com que o trabalhador entre em falha.
No Polly moderno (v8+), a API é ResiliencePipelineBuilder. Combine um tempo de espera curto com um orçamento de tentativas reduzido, para que um proxy inoperante falhe rapidamente e um instável tenha uma segunda oportunidade:
using Polly;
using Polly.Retry;
using Polly.Timeout;
var pipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddRetry(new RetryStrategyOptions<HttpResponseMessage>
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromMilliseconds(500),
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
.Handle<HttpRequestException>()
.Handle<TaskCanceledException>()
.HandleResult(r => (int)r.StatusCode >= 500 || (int)r.StatusCode == 408)
})
.AddTimeout(TimeSpan.FromSeconds(15))
.Build();
var response = await pipeline.ExecuteAsync(
async ct => await client.GetAsync(target, ct));Três dicas de calibração. Mantenha MaxRetryAttempts pequenas (três são suficientes); um proxy instável raramente vale uma quarta tentativa. UseJitter = true é importante quando se está a executar centenas de trabalhadores em paralelo, caso contrário, todos tentam novamente em sincronia e sobrecarregam o mesmo backend. E não inclua 407 na lista de tentativas, porque se as credenciais estiverem erradas uma vez, estarão erradas na próxima tentativa também, e acabará por esgotar o seu orçamento mais rapidamente. Verifique a superfície v8 em relação à documentação do Polly se estiver a atualizar da v7, uma vez que vários nomes de classes mudaram e o estilo v7 Policy.HandleAsync não compila com os novos construtores.
Seleção de proxy por pedido e regras de bypass
Um único proxy estático funciona para projetos de hobby. No momento em que começa a misturar tráfego interno e externo, ou a encaminhar domínios diferentes através de IPs de saída diferentes, precisa de seleção por pedido. WebProxy oferece-lhe duas opções: BypassList e uma IWebProxy .
BypassList aceita padrões regex. Qualquer coisa que corresponda ignora completamente o proxy, o que é a forma de manter nomes de host internos e intervalos CIDR privados fora do salto externo:
var proxy = new WebProxy("http://proxy:8080")
{
BypassProxyOnLocal = true,
BypassList = new[] { @"^.*\.internal\.example\.com$", @"^10\.0\.0\..*$" }
};Para um encaminhamento verdadeiramente por host, implemente IWebProxy você mesmo:
sealed class HostBasedProxy : IWebProxy
{
public ICredentials? Credentials { get; set; }
public Uri? GetProxy(Uri destination) =>
destination.Host.EndsWith("google.com") ? new Uri("http://us-proxy:8080")
: destination.Host.EndsWith("yandex.ru") ? new Uri("http://eu-proxy:8080")
: null;
public bool IsBypassed(Uri host) => GetProxy(host) is null;
}Isso é suficiente para controlar o roteamento geográfico a partir de um único HttpClient. O manipulador chama GetProxy para cada pedido, pelo que a decisão é dinâmica e não é necessário um cliente separado por região.
Depuração de erros comuns do proxy HttpClient
Quando algo corre mal, a exceção raramente é autoexplicativa. O caminho mais rápido para uma correção é partir dos sintomas.
|
Sintoma (o que se vê) |
Causa provável |
Solução de uma linha |
|---|---|---|
|
|
Credenciais de proxy em falta ou incorretas |
Defina |
|
Status |
O proxy está ativo, o upstream está inativo ou está a limitar a sua velocidade |
Tente novamente com backoff; mude para um IP diferente após a segunda falha |
|
|
Proxy recusado |
Confirme |
|
|
Proxy com terminação TLS sem um validador fixado, ou um certificado genuinamente inválido |
Fixar uma impressão digital com |
|
|
Falha na pesquisa de DNS dentro do proxy ou para o próprio proxy |
Verifique o nome do host; em clientes de longa duração, defina |
|
O pedido fica em espera até |
Proxy inativo, redirecionamento infinito ou bloqueado |
Defina |
|
Esporádico |
Vazamento de manipuladores de novos |
Mudar para |
Em caso de dúvida, registe o URL do proxy juntamente com a exceção. Metade de todos os erros de proxy desaparecem no momento em que se consegue ver qual o IP que realmente falhou, em vez de adivinhar entre um conjunto de cinquenta.
Escolher a estratégia de proxy certa para a sua carga de trabalho
Não existe uma resposta universal para como utilizar um proxy com o HttpClient em C# em grande escala. A questão é apenas quanto esforço de engenharia pretende dedicar à camada de proxy em comparação com a camada de dados. Escolha a opção mais simples que ainda cumpra os seus requisitos de fiabilidade.
|
Estratégia |
Confiabilidade |
Custo de manutenção |
Segmentação geográfica |
Quando escolher |
|---|---|---|---|---|
|
Proxies públicos gratuitos |
Muito baixo; muitos são honeypots |
Elevado; rotatividade constante |
Nenhuma |
Nunca para produção. Apenas para experiências locais. |
|
Proxies estáticos autenticados em centros de dados |
Adequado para alvos inofensivos |
Baixo |
Limitado |
APIs B2B, ferramentas internas, desbloqueio geográfico leve |
|
Rotação DIY através de um conjunto de endereços residenciais |
Elevado, se bem concebido |
Elevado; você controla as tentativas de repetição, verificações de integridade, sessões persistentes e contabilidade |
Sim, se o seu fornecedor disponibilizar tags de país |
Equipas com vontade de gerir o seu próprio pool e orçamento para a engenharia |
|
API de scraping/proxy gerida |
Alta; o fornecedor absorve as falhas |
Baixa; você chama um único endpoint |
Sim, geralmente por país |
Scraping em escala, alvos anti-bot, equipas pequenas |
Uma verificação intuitiva útil: se o código de proxy no seu repositório está a crescer mais rapidamente do que o código de análise, está a pagar a engenheiros para serem uma versão inferior de um fornecedor gerido. Suba na pilha. Por outro lado, se precisar apenas de alguns IPs estáticos para comunicar com uma API de um parceiro, não complique demasiado; um único WebProxy é suficiente.
Escalar além do «faça você mesmo»: encaminhar o HttpClient através do modo proxy da WebScrapingAPI
Quando a matemática do «faça você mesmo» deixa de fazer sentido, a saída mais limpa é manter o seu HttpClient e simplesmente apontá-lo para um ponto de extremidade de proxy gerido. O WebScrapingAPI expõe um gateway em modo proxy que aceita a mesma WebProxy + NetworkCredential receita que já escreveu, com rotação, segmentação geográfica e tratamento anti-bot absorvidos no lado do servidor.
var proxy = new WebProxy("http://proxy.webscrapingapi.com:80")
{
Credentials = new NetworkCredential(
"YOUR_API_KEY", // username slot
"render_js=false.country=us" // password slot carries options
)
};
var handler = new SocketsHttpHandler
{
Proxy = proxy,
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(2),
ConnectTimeout = TimeSpan.FromSeconds(15)
};
using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(60) };
var html = await client.GetStringAsync("https://example.com/product/42");A estrutura é idêntica ao padrão de proxy autenticado apresentado anteriormente neste artigo; a chave da API fica no campo do nome de utilizador e as opções de pedido no campo da palavra-passe. Não há ciclo de rotação porque o gateway escolhe um IP de saída novo por pedido, não há pipeline de repetição porque as respostas falhadas não são cobradas e não há lógica geográfica no seu código porque a seleção do país é um sinalizador. Ainda pode manter o seu pipeline Polly se quiser defesa em profundidade, mas a área de superfície que mantém diminui drasticamente. Trate isto como uma opção na matriz acima, não como um veredicto; é a escolha certa quando a sua equipa é pequena e os alvos são hostis.
Pontos-chave
- Configure o manipulador, não o cliente. Um proxy vive
HttpClientHandlerouSocketsHttpHandleratravés de umWebProxy.HttpClientele próprio não temProxypropriedade, razão pela qual não é possível alterá-lo em tempo de execução. - Passe sempre as credenciais através de
NetworkCredential. A incorporaçãouser:pass@na URL do proxy é silenciosamente descartada e é a causa mais comum de erros 407 misteriosos. - Use
IHttpClientFactorypara rotação. UmforeachLoop que cria umHttpClientpor proxy irá causar fugas de sockets sob carga. Clientes nomeados por proxy, além dePooledConnectionLifetime, corrija isso. - É preferível
SocketsHttpHandlerdiretamente em produção. Isso expõeConnectTimeout,PooledConnectionLifetimee o suporte a SOCKS5, de que acabará por precisar. - Não desative a validação TLS. Para proxies com terminação TLS, defina uma impressão digital. Para túneis CONNECT ou SOCKS, mantenha a validação ativada; falhas nesses casos são bugs reais, não ruído.
Perguntas frequentes: perguntas sobre proxies do HttpClient que os programadores realmente fazem
O HttpClient deteta automaticamente o proxy do sistema ou a variável de ambiente HTTPS_PROXY, e como posso desativar isso?
Sim. Sem nenhum manipulador atribuído, HttpClient usa o proxy padrão do sistema, que no .NET Core 3.1+ também lê HTTP_PROXY, HTTPS_PROXYe NO_PROXY no Linux e no macOS. Para desativar, passe um manipulador explícito com UseProxy = false, ou defina HttpClient.DefaultProxy = new WebProxy() na inicialização.
Posso alterar o proxy numa instância HttpClient existente ou preciso de uma nova?
Precisa de um novo cliente. O proxy é vinculado ao manipulador no momento da construção, e HttpClient não expõe um Proxy setter. Use um conjunto de clientes pré-configurados fornecidos pelo IHttpClientFactory, ou um IWebProxy cujo GetProxy(Uri) decida dinamicamente enquanto o manipulador permanece o mesmo.
Por que é que o meu pedido devolve 407 Proxy Authentication Required, mesmo tendo definido credenciais?
Três suspeitos habituais: credenciais incorporadas na URL (silenciosamente descartadas pelo WebProxy), uma palavra-passe codificada por URL duas vezes algures a montante, ou credenciais atribuídas a HttpClientHandler.Credentials em vez de WebProxy.Credentials. Apenas esta última alimenta o proxy. PreAuthenticate não ajuda neste caso; esse sinalizador controla o servidor de destino.
O HttpClient suporta proxies SOCKS5 no .NET 6 e versões posteriores?
Sim. SocketsHttpHandler Adicionou suporte nativo a SOCKS4, SOCKS4a e SOCKS5 a partir do .NET 6. Use um socks5://host:port URI no seu WebProxy e atribua credenciais através de NetworkCredential se o servidor SOCKS exigir autenticação de utilizador/palavra-passe.
Qual é a forma correta de cancelar uma solicitação proxy lenta sem vazar sockets?
Passe um CancellationToken de um CancellationTokenSource com um tempo de espera razoável e deixe a solicitação ser processada no OperationCanceledException. Emparelhe o token com SocketsHttpHandler.ConnectTimeout para que o handshake TCP falhe rapidamente e o socket retorne ao pool em vez de ficar pendente.
Conclusão
Isso é praticamente tudo o que precisa de saber sobre como usar um proxy com o HttpClient em C# sem se colocar numa situação sem saída. A estrutura da solução quase não muda de uma demonstração de cinco linhas para um worker de produção: um WebProxy, um handler, um HttpClient. O que muda é tudo à sua volta. O código de produção usa IHttpClientFactory para que os handlers sejam reciclados, define um ConnectTimeout para que os proxies inativos falhem rapidamente, fixa as impressões digitais TLS em vez de desativar a validação e envolve os pedidos num pipeline Polly para que falhas transitórias não acordem ninguém às 3 da manhã.
A matriz de decisão apresentada anteriormente neste artigo é a lição mais importante. Os proxies gratuitos não são gratuitos quando se contabiliza o tempo de engenharia. Os proxies estáticos de datacenter são ótimos até que o seu alvo implemente uma pilha anti-bot robusta. A rotação DIY é gratificante de construir, mas cara de manter. As APIs de proxy geridas trocam um orçamento de créditos pelo tempo que, de outra forma, gastaria em verificações de integridade, novas tentativas e tratamento de abusos.
Se a sua equipa chegou ao ponto em que a camada de proxy está a consumir mais tempo do que a camada de análise, o endpoint de proxy da WebScrapingAPI encaixa na mesma WebProxy + NetworkCredential fórmula que já tem, e só paga pelas respostas bem-sucedidas. Seja qual for o caminho que escolher, mantenha a abstração limpa: o manipulador na base, as tentativas no meio e a sua lógica de negócio no topo. O seu eu do futuro vai agradecer ao seu eu do presente por essa separação.




