Kurz gesagt: Um einen Proxy mit HttpClient in C# zu verwenden, erstellen Sie eineWebProxy, fügen Sie ihn einemHttpClientHandler(oderSocketsHttpHandler) und übergeben Sie diesen Handler an denHttpClientKonstruktor. Tauschen Sie in der Produktion manuelle Schleifen gegenIHttpClientFactory, fügen SieNetworkCredentialfür authentifizierte Proxys hinzu und verpacke Aufrufe in Wiederholungsversuche mit Polly, damit tote IPs deinen Worker nicht lahmlegen.
Einleitung
Wenn Sie schon einmal versucht haben, eine Website zu scrapen, auf eine regional gesperrte API gestoßen sind oder einen Dienst von mehreren Ausgangs-IPs aus einem Stresstest unterzogen haben, wissen Sie bereits, warum wir hier sind. Dieser Leitfaden führt Sie durch die Verwendung eines Proxys mit HttpClient in C#, von einer fünfzeiligen WebProxy Einrichtung bis hin zu einem Rotationspool, der keine Sockets verliert.
Ein HttpClient-Proxy ist einfach ein HttpClient dessen Handler mit einer WebProxy, sodass ausgehende Anfragen über eine Zwischen-IP getunnelt werden, anstatt direkt zum Ziel zu gehen. Das ist die gesamte Abstraktion. Alles andere – Authentifizierung, SOCKS5, SSL-Validierung, Rotation, Wiederholungsversuche – ist Konfiguration rund um diese Kernidee.
Wir gehen davon aus, dass Sie mit async/await und der dotnet CLI einer aktuellen .NET-Version vertraut sind. Wir gehen nicht davon aus, dass Sie den Quellcode von SocketsHttpHandler. Am Ende verfügen Sie über kopier- und einfügbare Muster für nicht authentifizierte Proxys, authentifizierte Proxys, SOCKS5, Rotation mit IHttpClientFactory, sichere TLS-Validierung, wenn ein Proxy im Pfad liegt, sowie eine Tabelle zur Fehlerbehebung für die Fehler, die in der Produktion unweigerlich auftreten werden. Am Ende gibt es außerdem eine Entscheidungsmatrix, damit Sie die Pflege Ihres eigenen Pools einstellen können, wenn sich der Aufwand nicht mehr lohnt. Wenn Sie sich einen Überblick über das Thema Web-Scraping verschaffen möchten, passt unser Einführungsleitfaden zum Erstellen eines Web-Scrapers mit C# gut zu diesem hier.
Ein mentales Modell für die Verwendung eines Proxys mit HttpClient in C#
Bevor Sie mit dem Code beginnen, sollten Sie die Schichtung richtig einrichten. HttpClient ist ein dünner Wrapper. Der eigentliche Transport, einschließlich der Proxy-Auflösung, findet in seinem Handler statt. In modernem .NET ist das entweder HttpClientHandler (die Legacy-freundliche Fassade) oder SocketsHttpHandler (die zugrunde liegende Engine). Beide stellen eine Proxy Eigenschaft vom Typ IWebProxy, und die integrierte Implementierung ist WebProxy.
Der Ablauf sieht wie folgt aus:
HttpClient
|
v
HttpMessageHandler (HttpClientHandler / SocketsHttpHandler)
| Proxy = IWebProxy
v
WebProxy -> proxy server -> upstream targetAus dieser Schichtung ergeben sich zwei Konsequenzen. Erstens ist der Proxy an den Handler gebunden, nicht an den Client. Du kannst HttpClient.Proxy, da es keine solche Eigenschaft gibt. Wenn Sie einen anderen Proxy wünschen, benötigen Sie einen anderen Handler und somit eine andere HttpClient (oder besser noch eine Factory, die diese für Sie bereitstellt).
Zweitens: Wenn Sie keinen Handler zuweisen, greift .NET auf die Standard-Proxy-Auflösung des Systems zurück, einschließlich Umgebungsvariablen wie HTTPS_PROXY. Das ist auf einem Entwickler-Laptop praktisch, in einem Container jedoch überraschend, daher werden wir später auf das Deaktivieren zurückkommen. Das Gleiche gilt, wenn ein Kollege HttpClient.DefaultProxy irgendwo im gemeinsamen Startcode festlegt: Jeder danach erstellte Client erbt diesen, es sei denn, du überschreibst den Handler.
In diesem Artikel behandeln wir jeden funktionierenden Proxy als http://host:port, mit optionalen Anmeldedaten. Wann immer Sie unten sehen, wie man einen Proxy mit HttpClient in C# verwendet, ist das das Muster, um das wir unsere Konfiguration herum aufbauen.
Einrichten eines minimalen C#-Projekts für Proxy-Tests
Überprüfen Sie Ihre Toolchain und erstellen Sie dann eine Konsolenanwendung. Wir führen alles auf einem aktuellen LTS .NET SDK aus (die Beispiele wurden für .NET 8 geschrieben und verhalten sich zum Zeitpunkt der Erstellung dieses Artikels auf späteren Versionen genauso).
dotnet --version # expect 8.x or newer
mkdir httpclient-proxy && cd httpclient-proxy
dotnet new consoleÖffnen Sie den Ordner in einem beliebigen Editor. Der Übersichtlichkeit halber Program.cs . Machen Sie Main async, damit wir await HTTP-Aufrufe ohne .Result Probleme:
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 ist der günstigste IP-Echo-Endpunkt, den es gibt. Führen Sie dotnet run, notieren Sie sich die IP-Adresse und behalten Sie diese als Ausgangsbasis bei. Sobald Sie einen Proxy einbinden, sollte derselbe Aufruf die Ausgangs-IP des Proxys anstelle Ihrer eigenen ausgeben. Ist dies nicht der Fall, liegt ein Konfigurationsfehler vor, kein Netzwerkfehler.
Konfigurieren eines nicht authentifizierten WebProxys mit HttpClientHandler
Beginnen Sie mit dem einfachsten Fall: einem kostenlosen oder lokalen Proxy, der keine Anmeldedaten erfordert. Das Rezept für die Verwendung eines Proxys mit HttpClient in C# besteht aus drei Objekten in dieser Reihenfolge: 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}");Ein paar Details, die leicht übersehen werden. Der WebProxy Konstruktor akzeptiert entweder eine Uri oder eine Zeichenfolge, und das Schema ist entscheidend: http:// für HTTP- und HTTPS-Proxys, die CONNECT verwenden, und (wie wir später sehen werden) socks5:// für SOCKS. Wenn Sie Anmeldedaten in die URL einbetten, WebProxy werden diese ignoriert, also machen Sie sich keine Mühe. BypassProxyOnLocal = true ist eine nützliche Standardeinstellung; sie bewahrt Sie davor, versehentlich Zustandsprüfungen über eine externe IP zu tunneln. UseDefaultCredentials = false verhindert, dass Windows die Identität des aktuellen Benutzers automatisch an einen Proxy eines Drittanbieters sendet – eine Art von Fehler, die man nur entdeckt, wenn bei einer Sicherheitsüberprüfung die erfassten Pakete analysiert werden.
Dies ist das Standardmuster. Alles andere in diesem Leitfaden sind Variationen desselben Drei-Objekt-Rezepts. Wenn du eine ausführlichere Erklärung darüber suchst, welche Proxy-Typen speziell für Scraping-Workloads sinnvoll sind, ist der Leitfaden zu den besten Proxy-Typen für Web-Scraping eine gute Lektüre.
Authentifizierung von Proxys: NetworkCredential, 407-Fehler und PreAuthenticate
Die meisten kostenpflichtigen Proxys erfordern eine Authentifizierung. In .NET wird dies NetworkCredential, angehängt an das WebProxy selbst, nicht an den Handler:
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);Zwei Fallstricke stellen fast jeden beim ersten Mal vor Probleme.
Fügen Sie keine Anmeldedaten in die URL ein. new WebProxy("http://user:pass@host:8080") wird den user:pass Teil. Der „userinfo“-Teil wird aus der Uri , wird aber niemals als Proxy-Anmeldedaten verwendet. Übergeben Sie immer eine NetworkCredential.
PreAuthenticate ist für das Ziel, nicht für den Proxy. Wenn der Proxy Sie ablehnt, gibt er den HTTP-Status 407 „Proxy Authentication Required“ zurück. HttpClient zeigt dies als HttpRequestException. Das Umschalten von HttpClientHandler.PreAuthenticate = true ändert dieses Verhalten nicht, da dieses Flag steuert, ob der Zielserver bei Folgeanfragen einen präventiven Authorization Header bei Folgeanfragen erhält. Es hat nichts mit dem Proxy-Authorization Header, den der Handler selbst verwaltet, sobald Sie Credentials.
Wenn Sie trotz scheinbar korrekter Anmeldedaten immer wieder den Status 407 erhalten, überprüfen Sie nacheinander drei Dinge: Senden Sie diese an den richtigen Host (manche Anbieter trennen die Steuerungsebene vom Gateway), ist Ihr Passwort irgendwo im Upstream URL-kodiert und ist Ihr Konto noch aktiv? Unser Artikel über häufige Proxy-Statusfehler und deren Identifizierung geht tiefer ins Detail, falls Sie eine Übersicht über die breitere Palette von Proxy-Fehlern benötigen.
Auswahl eines Protokolls: HTTP-, HTTPS- und SOCKS5-Proxys in C#
HttpClient Es spielt keine Rolle, ob das Ziel HTTP oder HTTPS ist, aber das Proxy-Protokoll ist wichtig, da es die Art und Weise beeinflusst, wie die Verbindung hergestellt wird.
- HTTP-Proxy (
http://...): Bei HTTP-Zielen kann der Proxy die Anfrage lesen und umschreiben. Bei HTTPS-Zielen sendet der Client eineCONNECTund tunnelt TLS durchgehend über den Proxy. - HTTPS-terminierender Proxy: Ein Sonderfall, bei dem der Proxy dem Client sein eigenes TLS-Zertifikat vorlegt und eine separate TLS-Verbindung zum Upstream-Server herstellt. So funktionieren einige kommerzielle Scraping-APIs im Proxy-Modus. Die Auswirkungen auf SSL behandeln wir im folgenden Abschnitt.
- SOCKS-Proxy (
socks5://,socks4://,socks4a://): Ein Tunnel auf der Transportschicht, der HTTP nicht versteht. Alles, was Sie über einen TCP-Socket senden können, wird durchgelassen.
In modernen .NET-Versionen SocketsHttpHandler mit integrierter Unterstützung für SOCKS4, SOCKS4a und SOCKS5 ausgeliefert (hinzugefügt mit .NET 6, laut Runtime-Issue-Tracker; überprüfen Sie dies anhand der SocketsHttpHandler-Dokumentation, wenn Sie eine Nicht-LTS-Preview-Version verwenden). Die Konfiguration entspricht dem WebProxy Rezept, mit einem anderen Schema:
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);Wenn Sie einen Proxy mit HttpClient in C# für einen SOCKS-Endpunkt verwenden müssen, ist dies das richtige Muster. SOCKS5 mit Benutzername/Passwort-Authentifizierung ist der De-facto-Standard für private Internetanbieter; SOCKS4 ist meist veraltet.
Sauberes Rotieren von Proxys mit IHttpClientFactory
Die Rotation von IPs ist der Punkt, an dem die meisten Tutorials stillschweigend scheitern. Der naive Ansatz sieht so aus:
// 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);
}Dieser Code verursacht Socket-Lecks. Jeder HttpClient hält seinen Handler am Leben, und jeder Handler hält an einem zugrunde liegenden Verbindungspool fest. Wenn Sie ein paar Tausend davon in einer Schleife starten, werden Sie die temporären Ports erschöpfen, was unter Linux als SocketException: Address already in use und unter Windows als wahrhaft kreative WinHttpException Spuren.
Die Lösung ist IHttpClientFactory. Es verwaltet die Lebensdauer der Handler für Sie, recycelt sie nach einem Zeitplan und ermöglicht es Ihnen, pro Proxy einen benannten oder typisierten Client zu registrieren. Ein kleines DI-Setup sieht so aus:
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>();Jetzt kannst du pro Anfrage einen Proxy auswählen, ohne dass etwas verloren geht:
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);
}Round-robin ist eine Änderung von nur einer Zeile: Führen Sie einen Interlocked.Increment Zähler und wende den Modulo-Operator auf proxyPool.Count. So oder so trifft jede Anfrage auf einen funktionsfähigen, gepoolten Handler, und IHttpClientFactory rotiert die zugrunde liegenden Handler auf PooledConnectionLifetime, wodurch das Problem der langlebigen veralteten DNS-Einträge umgangen wird, das wir im nächsten Abschnitt behandeln werden. Wenn Sie einen umfassenderen Überblick über Rotationsmuster und deren Anwendungsfälle wünschen, behandelt unser ausführlicher Artikel über rotierende Proxys die algorithmische Seite im Detail. Beachten Sie, dass IHttpClientFactory sich API-Spezifikationen, einschließlich DI-Lebensdauern und Polly-Integration, zwischen Hauptversionen ändern können; überprüfen Sie die Microsoft Learn-Seite zu IHttpClientFactory, wenn Sie eine ältere Laufzeitumgebung festlegen.
SocketsHttpHandler vs. HttpClientHandler für die Produktion
In modernen .NET-Umgebungen (Core 2.1+ und jedem dotnet new heutigen Projekt) HttpClientHandler ist meist ein Kompatibilitäts-Shim, der intern an SocketsHttpHandler unter der Haube delegiert. Für die meisten Demos sind die beiden austauschbar. Für langlebige Worker und Scraper sollten Sie SocketsHttpHandler direkt verwenden, da es die wichtigen Einstellmöglichkeiten bereitstellt:
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
};Die beiden Einstellungen, die man sich merken sollte:
PooledConnectionLifetimesteuert, wie lange eine Verbindung im Pool bestehen bleiben darf, bevor sie geschlossen wird. Die Auswirkungen auf den Proxy / die DNS-Aktualisierung sind der eigentliche Grund, warum man dies einstellt. Ein langlebigerHttpClienthält standardmäßig eine einzelne TCP-Verbindung für immer aufrecht, und DNS-Änderungen auf der Upstream-Seite (sehr häufig bei rotierenden Endpunkten für Privatanwender) werden nie übernommen. Zwei Minuten sind ein vernünftiger Standardwert für Scraper.ConnectTimeoutist eine vonHttpClient.Timeout. Letztere gilt für die gesamte Anfrage, erstere nur für den TCP-Handshake zum Proxy. Eine strenge Einstellung (5 bis 10 Sekunden) ist der kostengünstigste Weg, um zu verhindern, dass tote Proxys Worker-Threads monopolisieren.
AutomaticDecompression hat nichts mit Proxys zu tun, ist aber nützlich genug, um hier erwähnt zu werden, da die meisten Scraping-Endpunkte Antworten mit gzip komprimieren. Die Semantik der Eigenschaften rund um PooledConnectionLifetime und verwandten Elementen weicht zwischen größeren Laufzeitversionen ab; überprüfen Sie daher die Dokumentation, wenn Sie .NET 6 oder 7 verwenden.
Korrekte Handhabung der SSL/TLS-Validierung, wenn sich ein Proxy im Pfad befindet
Ein Proxy im Anforderungspfad verkompliziert TLS, aber die Regel ist einfach: Deaktivieren Sie die Validierung standardmäßig niemals. DangerousAcceptAnyServerCertificateValidator existiert, weil Microsoft ausdrücklich klarstellen wollte, dass Sie gefälschte Zertifikate akzeptieren, wenn Sie diese Option setzen. Bei kostenlosen oder gemeinsam genutzten Proxys ist das eine regelrechte Man-in-the-Middle-Sicherheitslücke, die nur darauf wartet, von demjenigen ausgenutzt zu werden, der den Proxy betreibt.
Es gibt zwei unterschiedliche Fälle, die man voneinander trennen muss.
CONNECT- und SOCKS-Tunnel übertragen Ihre TLS-Daten von Ende zu Ende. Das angezeigte Zertifikat ist das echte Zertifikat der Zielseite. Die Validierung sollte aktiviert bleiben, Punkt. Wenn hier ein SSL-Handshake-Fehler auftritt, ist der Proxy falsch konfiguriert oder das Upstream-Zertifikat ist tatsächlich ungültig. Vertuschen Sie das nicht.
TLS-terminierende Proxys (einige Scraping-APIs laufen in diesem Modus) schließen den Handshake absichtlich selbst ab und legen ihr eigenes Zertifikat vor. In diesem Fall ist die Akzeptanz einer unbekannten Zertifizierungsstelle Teil der Vereinbarung, jedoch nur für diesen spezifischen Proxy. Das sichere Muster ist ein Fingerabdruck oder ein CA-gebundener Callback:
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);
}
}
};Das ist immer noch eine Lockerung, aber eine begrenzte: Nur das Zertifikat, das mit dem festgelegten Fingerabdruck übereinstimmt, wird akzeptiert, und der Rest der Welt muss weiterhin die normale Kettenvalidierung bestehen. Wenn Sie in großem Maßstab scrapen und einen Proxy mit HttpClient in C# gegenüber einem TLS-terminierenden Gateway verwenden müssen, ist dies die produktionssichere Vorgehensweise.
Wiederholungsversuche, Timeouts und exponentielles Backoff mit Polly
Proxys fallen aus. Private IP-Adressen gehen mitten in der Sitzung offline, Rechenzentrumsbereiche werden null-geroutet, Upstream-Ziele begrenzen Ihre Rate für zehn Minuten und sind dann wieder erreichbar. Die richtige Reaktion ist ein Wiederholungsversuch mit Backoff, nicht der Absturz des Workers.
In der modernen Polly-Version (v8+) lautet die API ResiliencePipelineBuilder. Kombinieren Sie ein kurzes Timeout mit einem kleinen Wiederholungsbudget, damit ein ausgefallener Proxy schnell scheitert und ein unzuverlässiger eine zweite Chance erhält:
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));Drei Tipps zur Kalibrierung. Halte MaxRetryAttempts klein (drei sind ausreichend); ein unzuverlässiger Proxy ist selten einen vierten Versuch wert. UseJitter = true ist wichtig, wenn du Hunderte von parallelen Workern ausführst, da sie sonst alle im Gleichschritt erneut versuchen und dasselbe Backend überlasten. Und nimm 407 nicht in die Liste der wiederholbaren Versuche auf, denn wenn die Anmeldedaten einmal falsch sind, sind sie auch beim nächsten Versuch falsch, und du verbrauchst dein Budget nur schneller. Überprüfe die v8-Oberfläche anhand der Polly-Dokumentation, wenn du von v7 aktualisierst, da sich mehrere Klassennamen geändert haben und der v7 Policy.HandleAsync Stil lässt sich nicht mit den neuen Buildern kompilieren.
Proxy-Auswahl und Bypass-Regeln pro Anfrage
Ein einzelner statischer Proxy reicht für Hobbyprojekte aus. Sobald du jedoch intern und externen Datenverkehr mischst oder verschiedene Domains über unterschiedliche Ausgangs-IPs weiterleitest, benötigst du eine Auswahl pro Anfrage. WebProxy bietet dir zwei Hebel: BypassList und eine benutzerdefinierte IWebProxy Implementierung.
BypassList akzeptiert Regex-Muster. Alles, was mit diesen Mustern übereinstimmt, umgeht den Proxy vollständig. So halten Sie interne Hostnamen und private CIDR-Bereiche vom externen Hop fern:
var proxy = new WebProxy("http://proxy:8080")
{
BypassProxyOnLocal = true,
BypassList = new[] { @"^.*\.internal\.example\.com$", @"^10\.0\.0\..*$" }
};Für echtes Routing pro Host implementieren Sie IWebProxy selbst:
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;
}Das reicht aus, um das Geo-Routing von einem einzigen HttpClient. Der Handler ruft GetProxy für jede Anfrage auf, sodass die Entscheidung dynamisch erfolgt und Sie nicht für jede Region einen separaten Client benötigen.
Fehlerbehebung bei häufigen HttpClient-Proxy-Fehlern
Wenn etwas schiefgeht, ist die Ausnahme selten selbsterklärend. Der schnellste Weg zur Behebung ist, bei den Symptomen anzusetzen.
|
Symptom (was Sie sehen) |
Mögliche Ursache |
Einzeilige Lösung |
|---|---|---|
|
|
Fehlende oder falsche Proxy-Anmeldedaten |
Einstellungen |
|
Status |
Proxy ist aktiv, Upstream ist ausgefallen oder begrenzt deine |
Wiederholen mit Backoff; nach dem zweiten Fehlversuch auf eine andere IP umschalten |
|
|
Proxy abgelehnt |
Überprüfe |
|
|
TLS-terminierender Proxy ohne festgelegten Validator oder ein tatsächlich ungültiges Zertifikat |
Fügen Sie einen Fingerabdruck mit |
|
|
DNS-Abfrage innerhalb des Proxys oder für den Proxy selbst fehlgeschlagen |
Überprüfen Sie den Hostnamen; bei langlebigen Clients setzen Sie |
|
Die Anfrage hängt, bis |
Proxy ausgefallen, unendliche Weiterleitung oder hängend |
Stellen Sie |
|
Sporadisch |
Handler-Lecks aus neuen |
Umstellung auf |
Im Zweifelsfall protokollieren Sie die Proxy-URL zusammen mit der Ausnahme. Die Hälfte aller Proxy-Fehler verschwindet in dem Moment, in dem Sie sehen können, welche IP tatsächlich ausgefallen ist, anstatt aus einem Pool von fünfzig zu raten.
Die richtige Proxy-Strategie für Ihre Arbeitslast wählen
Es gibt keine allgemeingültige beste Antwort darauf, wie man einen Proxy mit HttpClient in C# in großem Maßstab einsetzt. Es stellt sich lediglich die Frage, wie viel Entwicklungsaufwand Sie in die Proxy-Ebene im Vergleich zur Datenebene investieren möchten. Wählen Sie die niedrigste Stufe, die Ihre Zuverlässigkeitsanforderungen noch erfüllt.
|
Strategie |
Zuverlässigkeit |
Wartungsaufwand |
Geotargeting |
Wann man sich dafür entscheiden sollte |
|---|---|---|---|---|
|
Kostenlose öffentliche Proxys |
Sehr gering; viele sind Honeypots |
Hoch; ständige Fluktuation |
Keine |
Niemals für den Produktionsbetrieb. Nur für lokale Experimente. |
|
Statische, authentifizierte Rechenzentrum-Proxys |
Angemessen für harmlose Ziele |
Niedrig |
Eingeschränkt |
B2B-APIs, interne Tools, leichte Geoblocking-Umgehung |
|
DIY-Rotation über einen privaten Pool |
Hoch, wenn gut umgesetzt |
Hoch; Sie sind für Wiederholungsversuche, Zustandsprüfungen, Sticky Sessions und Abrechnung verantwortlich |
Ja, wenn Ihr Anbieter Ländercodes bereitstellt |
Teams, die bereit sind, einen eigenen Pool zu betreiben und über das entsprechende Budget für die Entwicklung verfügen |
|
Verwaltetes Scraping/Proxy-API |
Hoch; der Anbieter fängt Fehler ab |
Niedrig; Sie rufen einen Endpunkt auf |
Ja, in der Regel pro Land |
Scraping in großem Maßstab, Anti-Bot-Maßnahmen, kleine Teams |
Ein nützlicher Test: Wenn der Proxy-Code in Ihrem Repo schneller wächst als der Parsing-Code, bezahlen Sie Entwickler dafür, eine schlechtere Version eines verwalteten Anbieters zu sein. Steigen Sie in der Stack-Hierarchie auf. Umgekehrt gilt: Wenn Sie nur eine Handvoll statischer IPs benötigen, um mit einer Partner-API zu kommunizieren, übertreiben Sie es nicht; eine einzige authentifizierte WebProxy ist ausreichend.
Skalierung jenseits von DIY: HttpClient über den WebScrapingAPI-Proxy-Modus leiten
Wenn die DIY-Rechnung nicht mehr aufgeht, ist der sauberste Ausweg, deinen HttpClient und es einfach auf einen Managed-Proxy-Endpunkt zu richten. WebScrapingAPI stellt ein Gateway im Proxy-Modus bereit, das dasselbe WebProxy + NetworkCredential Rezept akzeptiert, das Sie bereits geschrieben haben, wobei Rotation, Geo-Targeting und Anti-Bot-Handhabung serverseitig übernommen werden.
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");Die Struktur entspricht dem Muster des authentifizierten Proxys aus dem früheren Teil des Artikels; der API-Schlüssel befindet sich im Benutzernamenfeld und die Anfrageoptionen im Passwortfeld. Es gibt keine Rotationsschleife, da das Gateway pro Anfrage eine neue Ausgangs-IP auswählt, keine Wiederholungspipeline, da fehlgeschlagene Antworten nicht abgerechnet werden, und keine Geolokalisierungslogik in Ihrem Code, da die Länderauswahl über ein Flag erfolgt. Sie können Ihre Polly-Pipeline beibehalten, wenn Sie eine mehrschichtige Verteidigung wünschen, aber der zu verwaltende Umfang schrumpft drastisch. Betrachten Sie dies als eine Option in der obigen Matrix, nicht als endgültige Entscheidung; es ist die richtige Wahl, wenn Ihr Team klein ist und die Ziele feindlich gesinnt sind.
Wichtige Erkenntnisse
- Konfigurieren Sie den Handler, nicht den Client. Ein Proxy lebt von
HttpClientHandleroderSocketsHttpHandlerüber einenWebProxy.HttpClientselbst keineProxyEigenschaft, weshalb Sie ihn zur Laufzeit nicht ändern können. - Übergeben Sie Anmeldedaten immer über
NetworkCredential. Das Einbettenuser:pass@in die Proxy-URL wird stillschweigend ignoriert und ist die häufigste Ursache für mysteriöse 407-Fehler. - Verwenden Sie
IHttpClientFactoryfür die Rotation. EineforeachSchleife, die pro ProxyHttpClientproxy Sockets unter Last aus. Benannte Clients pro Proxy, plusPooledConnectionLifetime, beheben Sie das. - Ziehe es vor,
SocketsHttpHandlerdirekt in der Produktion. Es bietetConnectTimeout,PooledConnectionLifetimeund SOCKS5-Unterstützung, die du letztendlich alle benötigen wirst. - Deaktivieren Sie die TLS-Validierung nicht. Bei TLS-terminierenden Proxys sollten Sie einen Fingerabdruck festlegen. Bei CONNECT- oder SOCKS-Tunneln lassen Sie die Validierung aktiviert; Fehler dort sind echte Fehler, kein Rauschen.
FAQ: Fragen zu HttpClient-Proxys, die Entwickler tatsächlich stellen
Erkennt HttpClient den Systemproxy oder die Umgebungsvariable HTTPS_PROXY automatisch, und wie kann ich das deaktivieren?
Ja. Wenn kein Handler zugewiesen ist, HttpClient den Standard-Proxy des Systems, der unter .NET Core 3.1+ auch HTTP_PROXY, HTTPS_PROXYund NO_PROXY unter Linux und macOS. Um dies zu deaktivieren, übergeben Sie einen expliziten Handler mit UseProxy = falseoder legen Sie HttpClient.DefaultProxy = new WebProxy() beim Start.
Kann ich den Proxy bei einer bestehenden HttpClient-Instanz ändern, oder benötige ich eine neue?
Sie benötigen einen neuen Client. Der Proxy wird beim Erstellen an den Handler gebunden, und HttpClient stellt keinen Proxy Setter. Verwenden Sie einen Pool vorkonfigurierter Clients, die von IHttpClientFactory, oder einen benutzerdefinierten IWebProxy , dessen GetProxy(Uri) dynamisch entscheidet, während der Handler derselbe bleibt.
Warum gibt meine Anfrage den Status 407 „Proxy Authentication Required“ zurück, obwohl ich Anmeldedaten festgelegt habe?
Drei übliche Ursachen: In die URL eingebettete Anmeldedaten (die von WebProxy), ein Passwort, das irgendwo stromaufwärts doppelt URL-kodiert wurde, oder Anmeldedaten, die HttpClientHandler.Credentials statt WebProxy.Credentials. Nur Letzteres wird an den Proxy weitergeleitet. PreAuthenticate hilft hier nicht weiter; dieses Flag steuert den Zielserver.
Unterstützt HttpClient SOCKS5-Proxys unter .NET 6 und höher?
Ja. SocketsHttpHandler Ab .NET 6 wurde native Unterstützung für SOCKS4, SOCKS4a und SOCKS5 hinzugefügt. Verwenden Sie eine socks5://host:port URI in Ihrem WebProxy und weisen Sie Anmeldedaten über NetworkCredential , falls der SOCKS-Server eine Benutzername-/Passwort-Authentifizierung erfordert.
Wie kann man eine langsame Proxy-Anfrage ordnungsgemäß abbrechen, ohne Sockets freizugeben?
Übergeben Sie eine CancellationToken von einem CancellationTokenSource mit einem sinnvollen Timeout und die Anfrage auf OperationCanceledException. Koppeln Sie das Token mit SocketsHttpHandler.ConnectTimeout , damit der TCP-Handshake schnell fehlschlägt und der Socket in den Pool zurückkehrt, anstatt in der Schwebe zu bleiben.
Fazit
Das ist im Wesentlichen alles, was Sie wissen müssen, um einen Proxy mit HttpClient in C# zu verwenden, ohne sich in eine Sackgasse zu manövrieren. Die Struktur der Lösung ändert sich kaum von einer fünfzeiligen Demo zu einem Produktions-Worker: ein WebProxy, ein Handler, ein HttpClient. Was sich ändert, ist alles drum herum. Produktionscode verwendet IHttpClientFactory , sodass Handler recycelt werden, legt einen engen ConnectTimeout , damit tote Proxys schnell ausfallen, legt TLS-Fingerabdrücke fest, anstatt die Validierung zu deaktivieren, und verpackt Anfragen in eine Polly-Pipeline, damit vorübergehende Ausfälle niemanden um 3 Uhr morgens wecken.
Die Entscheidungsmatrix weiter oben im Artikel ist die wichtigste Erkenntnis. Kostenlose Proxys sind nicht kostenlos, wenn man die Entwicklungszeit mit einberechnet. Statische Rechenzentrumsproxys sind großartig, bis Ihr Ziel einen ernstzunehmenden Anti-Bot-Stack einsetzt. Eine selbst erstellte Rotation lohnt sich beim Aufbau, ist aber teuer im Betrieb. Verwaltete Proxy-APIs tauschen ein Guthabenbudget gegen die Zeit ein, die Sie sonst für Zustandsprüfungen, Wiederholungsversuche und die Behandlung von Missbrauch aufwenden würden.
Wenn Ihr Team den Punkt erreicht hat, an dem die Proxy-Ebene mehr Zeit in Anspruch nimmt als die Parsing-Ebene, fügt sich der WebScrapingAPI-Proxy-Endpunkt nahtlos in das WebProxy + NetworkCredential Rezept, das Sie bereits haben, und Sie zahlen nur für erfolgreiche Antworten. Welchen Weg Sie auch wählen, halten Sie die Abstraktion sauber: Handler ganz unten, Wiederholungsversuche in der Mitte, Ihre Geschäftslogik ganz oben. Ihr zukünftiges Ich wird Ihrem gegenwärtigen Ich für diese Trennung dankbar sein.




