Perché HttpClient BaseAddress non funziona?


299

Si consideri il seguente codice, dove BaseAddressdefinisce un percorso URI parziale.

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");
}

Mi aspetto che questo esegua una GETrichiesta a http://something.com/api/resource/7. Ma non lo fa.

Dopo alcune ricerche, trovo questa domanda e rispondo: HttpClient con BaseAddress . Il suggerimento è quello di porre /alla fine del 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");
}

Non funziona ancora. Ecco la documentazione: HttpClient.BaseAddress Cosa sta succedendo qui?



@ ГеоргийЛанец Il duplicato inverso è già stato proposto. Ho scritto questa domanda specificamente perché quell'altra domanda non è stata scritta in un modo che fosse molto rilevabile da persone con lo stesso problema, e ho scritto la risposta qui perché la risposta laggiù ha lasciato un punto importante.
Timothy Shields,

ma questa domanda viene posta più tardi
George Lanetz,

2
@ ГеоргийЛанец Non è così che funziona. Di solito la domanda più "canonica" è quella che ottiene i duplicati che la puntano. L'altra domanda riguardava un singolo problema che l'utente stava riscontrando invece di leggere come una FAQ.
Timothy Shields,

2
@ ГеоргийЛанец Nota anche che faccio riferimento a quell'altra domanda in questa domanda e spiego perché l'altra domanda e risposta non sono sufficienti per risolvere il problema.
Timothy Shields,

Risposte:


719

Si scopre che, tra le quattro possibili permutazioni di inclusione o esclusione di barre finali o di trascinamento in avanti sull'URI BaseAddresse sul relativo URI passate al GetAsyncmetodo - o qualsiasi altro metodo di HttpClient- funziona solo una permutazione. È necessario posizionare una barra alla fine di BaseAddresse non inserire una barra all'inizio dell'URI relativo, come nell'esempio seguente.

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");
}

Anche se ho risposto alla mia domanda, ho pensato che avrei contribuito alla soluzione qui poiché, ancora una volta, questo comportamento ostile non è documentato. Io e il mio collega abbiamo trascorso gran parte della giornata a cercare di risolvere un problema che è stato in definitiva causato da questa stranezza di HttpClient.


4
Grazie. Ciò ha risolto un problema con cui ho lottato per la maggior parte dei due giorni ormai, tra il passaggio ad Azure, il ritorno a IIS e il ritorno a IIS Express, che più sgarbatamente ignora i tagli fuori posto o extra. Una volta impostato nella mia classe di base RestClient, era quasi invisibile e non ha ricevuto alcuna attenzione, e non ho mai visto l'url completo nei miei punti di interruzione ecc.
ProfK

43
Posso confermare che questa stranezza (e questa correzione) è ancora rilevante in .NET Core. Grazie per aver ridotto Timothy.
Nate Barbettini,

8
Questo perché senza trascinare la barra quando genera richieste, lascia cadere l'ultima parte. Quindi colpisce qualcosa.com/resource/7 . Se si imposta l'indirizzo di base come qualcosa / com (non importa se con o senza barra finale) non importa se si inserisce barra all'inizio di api / resource / 7. Senza barra finale l'ultima parte dell'indirizzo di base viene trattata come un file e rilasciata durante la richiesta di bulding.
Piotr Perak,

12
Questo non affronta direttamente la domanda originale ma è correlato. Per Mircosoft, un'istanza di HttpClient () dovrebbe essere assegnata a una variabile statica e riutilizzata ( docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/… - Creating a new HttpClient instance per request can exhaust the available sockets). Quindi dovresti prendere in considerazione la rimozione di Using ().
sanmcp,

6
Un'implementazione orribile. Perché non lo risolvono?
timmkrause,

55

La risoluzione di riferimento è descritta da RFC 3986 Uniform Resource Identifier (URI): sintassi generica . Ed è esattamente come dovrebbe funzionare. Per preservare il percorso dell'URI di base è necessario aggiungere una barra alla fine dell'URI di base e rimuovere la barra all'inizio dell'URI relativo.

Se l'URI di base contiene un percorso non vuoto, la procedura di unione elimina la sua ultima parte (dopo l'ultima /). Sezione pertinente :

5.2.3. Unisci percorsi

Lo pseudocodice sopra si riferisce a una routine di "unione" per unire un riferimento di percorso relativo con il percorso dell'URI di base. Questo si ottiene come segue:

  • Se l'URI di base ha un componente di autorità definito e un percorso vuoto, restituisce una stringa composta da "/" concatenata con il percorso del riferimento; altrimenti

  • restituisce una stringa costituita dal componente del percorso del riferimento aggiunto a tutti tranne l'ultimo segmento del percorso dell'URI di base (ovvero, escludendo qualsiasi carattere dopo il più "/" più a destra nel percorso dell'URI di base o escludendo l'intero percorso dell'URI di base se esso non contiene alcun carattere "/").

Se l'URI relativo inizia con una barra, viene chiamato un URI relativo del percorso assoluto. In questo caso la procedura di unione ignora tutto il percorso URI di base. Per ulteriori informazioni, consultare 5.2.2. Sezione Trasforma riferimenti .


3
Bene, ma le librerie client come HttpClient dovrebbero proteggerci da dettagli esoterici di implementazione come questo.
Jamie Ide,

-1

Si è verificato un problema con HTTPClient, anche se i suggerimenti non riuscivano ancora ad autenticarlo. Ho scoperto che avevo bisogno di un '/' finale nel mio percorso relativo.

vale a dire

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
            });

-6

In alternativa, non usare BaseAddressaffatto. Inserisci l'intero URL in GetAsync()


33
Non risponde alla domanda.
Archibald,

7
BaseAddress riduce il rumore. Ai miei occhi comunque. :)
MetalMikester,

2
Dovrò essere in disaccordo con i commenti negativi. Ho trascorso 2 giorni a cercare di capire perché le mie chiamate HttpClient funzionano sul mio PC Dev ma si rompono sul server. Stranamente Powershell funziona ma .net no. Stavo usando .SendAsyc. Poi ho scoperto che .GetAsyc ha funzionato. Questo mi ha portato su una strada diversa e alla fine qui. L'aggiunta o la rimozione di / tra l'indirizzo di base e l'URL relativo non ha fatto nulla. Ho ancora 404 errori .... tuttavia quando non ho impostato l'indirizzo di base e ho inserito l'intero percorso nel relativo .. ha funzionato! Ancora una volta, questo è con .SendAsync ma ci sono stati 2 giorni che non tornerò mai più!
da_jokker,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.