Warum funktioniert HttpClient BaseAddress nicht?

299

Betrachten Sie den folgenden Code, in dem BaseAddressder einen partiellen URI-Pfad definiert.

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api");
    var response = await client.GetAsync("/resource/7");
}

Ich erwarte, dass dies eine GETAnfrage an http://something.com/api/resource/7. Aber das tut es nicht.

Nach einigem Suchen finde ich diese Frage und Antwort: HttpClient mit BaseAddress . Der Vorschlag ist, /am Ende des zu platzieren BaseAddress.

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api/");
    var response = await client.GetAsync("/resource/7");
}

Es funktioniert immer noch nicht. Hier ist die Dokumentation: HttpClient.BaseAddress Was ist hier los?

Timothy Shields
quelle
Mögliches Duplikat von HttpClient mit BaseAddress
George Lanetz
@ ГеоргийЛанец Das umgekehrte Duplikat wurde bereits vorgeschlagen. Ich habe diese Frage speziell geschrieben, weil diese andere Frage nicht auf eine Weise geschrieben wurde, die für Menschen mit demselben Problem sehr auffindbar war, und ich habe die Antwort hier geschrieben, weil die Antwort dort einen wichtigen Punkt ausgelassen hat.
Timothy Shields
aber diese Frage wird später gestellt
George Lanetz
2
@ ГеоргийЛанец So funktioniert das nicht. Normalerweise ist die "kanonischste" Frage die, bei der die Duplikate darauf verweisen. Diese andere Frage betraf ein einzelnes Problem, das der Benutzer hatte, anstatt wie eine FAQ zu lesen.
Timothy Shields
2
@ ГеоргийЛанец Beachten Sie auch, dass ich in dieser Frage auf diese andere Frage verweise und erkläre, warum die andere Frage und Antwort nicht ausreichen, um das Problem zu lösen.
Timothy Shields

Antworten:

719

Es stellt sich heraus, dass von den vier möglichen Permutationen des Einschlusses oder Ausschlusses von nachgestellten oder führenden Schrägstrichen auf dem BaseAddressund dem relativen URI, der an die GetAsyncMethode übergeben wird - oder welcher anderen Methode auch immer HttpClient- nur eine Permutation funktioniert. Sie müssen einen Schrägstrich am Ende des BaseAddressund keinen Schrägstrich am Anfang Ihres relativen URI setzen, wie im folgenden Beispiel.

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api/");
    var response = await client.GetAsync("resource/7");
}

Obwohl ich meine eigene Frage beantwortet habe, dachte ich, ich würde die Lösung hier einbringen, da dieses unfreundliche Verhalten wiederum nicht dokumentiert ist. Mein Kollege und ich verbrachten den größten Teil des Tages damit, ein Problem zu beheben, das letztendlich durch diese Seltsamkeit von verursacht wurde HttpClient.

Timothy Shields
quelle
4
Danke dir. Dies löste ein Problem, mit dem ich seit fast zwei Tagen zu kämpfen habe, zwischen dem Wechsel zu Azure, zurück zu IIS und zurück zu IIS Express, bei dem falsch platzierte oder zusätzliche Schrägstriche am grobsten ignoriert werden. Einmal in der Basisklasse von mir eingestellt RestClient, war es fast unsichtbar und bekam überhaupt keine Aufmerksamkeit, und ich sah nie die vollständige URL an meinen Haltepunkten usw.
ProfK
43
Ich kann bestätigen, dass diese Kuriosität (und dieses Update) in .NET Core immer noch relevant ist. Danke, dass du meinen haarsträubenden Timothy reduziert hast.
Nate Barbettini
8
Dies liegt daran, dass ohne letzten Schrägstrich beim Erstellen von Anforderungen der letzte Teil gelöscht wird. Also trifft es etwas.com/resource/7 . Wenn Sie die Basisadresse als etwas / com festlegen (egal ob mit oder ohne abschließenden Schrägstrich), spielt es auch keine Rolle, ob Sie einen Schrägstrich am Anfang von api / resource / 7 setzen. Ohne abschließenden Schrägstrich wird der letzte Teil der Basisadresse wie eine Datei behandelt und bei einer Bulding-Anfrage gelöscht.
Piotr Perak
12
Dies bezieht sich nicht direkt auf die ursprüngliche Frage, sondern ist damit verbunden. Laut Mircosoft sollte eine Instanz von HttpClient () einer statischen Variablen zugewiesen und wiederverwendet werden ( docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/… - Creating a new HttpClient instance per request can exhaust the available sockets). Sie sollten also in Betracht ziehen, Using () zu entfernen.
Sanmcp
6
Nur eine schreckliche Implementierung. Warum beheben sie das nicht?
Timmkrause
55

Die Referenzauflösung wird durch RFC 3986 Uniform Resource Identifier (URI): Generic Syntax beschrieben . Und genau so soll es funktionieren. Um den Basis-URI-Pfad beizubehalten, müssen Sie am Ende des Basis-URI einen Schrägstrich hinzufügen und am Anfang des relativen URI einen Schrägstrich entfernen.

Wenn der Basis-URI einen nicht leeren Pfad enthält, verwirft die Zusammenführungsprozedur den letzten Teil (nach dem letzten /). Relevanter Abschnitt :

5.2.3. Pfade zusammenführen

Der obige Pseudocode bezieht sich auf eine "Zusammenführungs" -Routine zum Zusammenführen einer Relativpfadreferenz mit dem Pfad des Basis-URI. Dies wird wie folgt erreicht:

  • Wenn der Basis-URI eine definierte Berechtigungskomponente und einen leeren Pfad hat, geben Sie eine Zeichenfolge zurück, die aus "/" besteht, das mit dem Pfad der Referenz verknüpft ist. Andernfalls

  • Geben Sie eine Zeichenfolge zurück, die aus der Pfadkomponente der Referenz besteht, die an alle bis auf das letzte Segment des Basis-URI-Pfads angehängt ist (dh ohne alle Zeichen nach dem am weitesten rechts stehenden "/" im Basis-URI-Pfad oder ohne den gesamten Basis-URI-Pfad, falls dies der Fall ist enthält keine "/" Zeichen).

Wenn der relative URI mit einem Schrägstrich beginnt, wird er als relativer URI für den absoluten Pfad bezeichnet. In diesem Fall ignorieren die Zusammenführungsprozeduren alle Basis-URI-Pfade. Weitere Informationen finden Sie unter 5.2.2. Abschnitt Referenzen transformieren .

Leonid Vasilev
quelle
3
Gut, aber Client-Bibliotheken wie HttpClient sollen uns vor solchen esoterischen Implementierungsdetails schützen.
Jamie Ide
-1

Stieß auf ein Problem mit dem HTTPClient, obwohl die Vorschläge ihn immer noch nicht zur Authentifizierung bringen konnten. Es stellte sich heraus, dass ich ein abschließendes '/' in meinem relativen Pfad brauchte.

dh

var result = await _client.GetStringAsync(_awxUrl + "api/v2/inventories/?name=" + inventoryName);
var result = await _client.PostAsJsonAsync(_awxUrl + "api/v2/job_templates/" + templateId+"/launch/" , new {
                inventory = inventoryId
            });
Tony
quelle
-6

Alternativ - überhaupt nicht verwenden BaseAddress. Geben Sie die gesamte URL in GetAsync() ein.

userSteve
quelle
33
Beantwortet die Frage überhaupt nicht.
Archibald
7
Die BaseAddress reduziert Rauschen. Für meine Augen sowieso. :)
MetalMikester
2
Ich muss den negativen Kommentaren widersprechen. Ich habe 2 Tage lang versucht herauszufinden, warum meine HttpClient-Aufrufe auf meinem Dev-PC funktionieren, aber auf dem Server nicht funktionieren. Seltsamerweise funktioniert Powershell, aber .net nicht. Ich habe .SendAsyc verwendet. Dann entdeckte ich, dass .GetAsyc funktioniert. Dies führte mich auf einen anderen Weg und schließlich hierher. Das Hinzufügen oder Entfernen des / zwischen der Basisadresse und der relativen URL hat nichts bewirkt. Ich habe immer noch 404 Fehler ... aber als ich die Basisadresse nicht festgelegt und den gesamten Pfad in das Relative eingefügt habe, hat es funktioniert! Auch dies ist mit .SendAsync, aber es gab 2 Tage, an denen ich nie zurückkomme!
da_jokker