Pourquoi HttpClient BaseAddress ne fonctionne pas?

299

Considérez le code suivant, où le BaseAddressdéfinit un chemin d'URI partiel.

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

Je m'attends à ce que cela effectue une GETdemande http://something.com/api/resource/7. Mais ce n'est pas le cas.

Après quelques recherches, je trouve cette question et réponse: HttpClient avec BaseAddress . La suggestion est de placer /à la fin du 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");
}

Ça ne marche toujours pas. Voici la documentation: HttpClient.BaseAddress Que se passe-t-il ici?

Timothy Shields
la source
Copie
George Lanetz
@ ГеоргийЛанец Le doublon inverse a déjà été proposé. J'ai écrit cette question spécifiquement parce que cette autre question n'a pas été écrite d'une manière très découvrable par les personnes ayant le même problème, et j'ai écrit la réponse ici parce que la réponse là-bas laissait de côté un point important.
Timothy Shields
mais cette question est posée plus tard
George Lanetz
2
@ ГеоргийЛанец Ce n'est pas ainsi que cela fonctionne. Habituellement, la question la plus "canonique" est celle qui fait pointer les doublons. Cette autre question concernait un seul problème que l'utilisateur avait au lieu de lire comme une FAQ.
Timothy Shields
2
@ ГеоргийЛанец Notez également que je fais référence à cette autre question dans cette question, et j'explique pourquoi l'autre question et la réponse sont insuffisantes pour résoudre le problème.
Timothy Shields

Réponses:

720

Il s'avère que, sur les quatre permutations possibles d'inclusion ou d'exclusion de barres obliques de fin ou de début sur l' BaseAddressURI relatif et transmises à la GetAsyncméthode - ou à toute autre méthode de HttpClient- une seule permutation fonctionne. Vous devez placer une barre oblique à la fin de BaseAddress, et vous ne devez pas placer de barre oblique au début de votre URI relatif, comme dans l'exemple suivant.

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

Même si j'ai répondu à ma propre question, j'ai pensé apporter la solution ici car, encore une fois, ce comportement hostile n'est pas documenté. Mon collègue et moi avons passé la majeure partie de la journée à essayer de résoudre un problème qui a finalement été causé par cette bizarrerie de HttpClient.

Timothy Shields
la source
4
Je vous remercie. Cela a résolu un problème avec lequel je luttais depuis presque deux jours maintenant, entre le passage à Azure, le retour à IIS et le retour à IIS Express, qui ignore le plus grossièrement les barres obliques déplacées ou supplémentaires. Une fois réglé dans la classe de base de mon RestClient, il était presque invisible et n'a attiré aucune attention, et je n'ai jamais vu l'URL complète à mes points d'arrêt, etc.
ProfK
43
Je peux confirmer que cette bizarrerie (et ce correctif) est toujours pertinente dans .NET Core. Merci d'avoir réduit mon Timothy qui tire les cheveux.
Nate Barbettini
8
En effet, sans barre oblique de fin lors de la génération des demandes, il supprime la dernière partie. Il frappe donc quelque chose.com/resource/7 . Si vous définissez l'adresse de base comme quelque chose / com (peu importe si avec ou sans barre oblique de fin) cela n'a pas d'importance si vous mettez la barre oblique au début de api / resource / 7. Sans barre oblique de fin, la dernière partie de l'adresse de base est traitée comme un fichier et supprimée lors de la création d'une requête.
Piotr Perak
12
Il ne s’agit pas directement de la question initiale, mais connexe. Par Mircosoft, une instance de HttpClient () doit être affectée à une variable statique et réutilisée ( docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/… - Creating a new HttpClient instance per request can exhaust the available sockets). Vous devriez donc envisager de supprimer Using ().
sanmcp
6
Juste une horrible mise en œuvre. Pourquoi ne résolvent-ils pas cela?
timmkrause
56

La résolution de référence est décrite par RFC 3986 Uniform Resource Identifier (URI): syntaxe générique . Et c'est exactement ainsi que cela devait fonctionner. Pour conserver le chemin de l'URI de base, vous devez ajouter une barre oblique à la fin de l'URI de base et supprimer la barre oblique au début de l'URI relatif.

Si l'URI de base contient un chemin non vide, la procédure de fusion ignore sa dernière partie (après la dernière /). Section pertinente :

5.2.3. Chemins de fusion

Le pseudocode ci-dessus fait référence à une routine de «fusion» pour fusionner une référence de chemin relatif avec le chemin de l'URI de base. Ceci est accompli comme suit:

  • Si l'URI de base a un composant d'autorité défini et un chemin vide, retournez une chaîne composée de "/" concaténée avec le chemin de la référence; autrement

  • retourne une chaîne composée du composant de chemin de référence ajouté à tous sauf le dernier segment du chemin de l'URI de base (c'est-à-dire, en excluant tous les caractères après le "/" le plus à droite dans le chemin de l'URI de base, ou en excluant le chemin de l'URI de base entier s'il ne contient aucun caractère "/").

Si l'URI relatif commence par une barre oblique, il est appelé un URI relatif à chemin absolu. Dans ce cas, la procédure de fusion ignore tous les chemins URI de base. Pour plus d'informations, consultez 5.2.2. Transformer la section Références .

Leonid Vasilev
la source
4
Bien, mais les bibliothèques clientes telles que HttpClient sont censées nous protéger des détails d'implémentation ésotérique comme celui-ci.
Jamie Ide
C'est formidable lorsque les réponses couvrent les raisons pour lesquelles quelque chose fonctionne de cette façon, avec des liens et des citations pertinents. Même si cela n'aide pas directement avec le problème de la question, cela aide beaucoup.
Rast
-1

Ran dans un problème avec le HTTPClient, même avec les suggestions n'a toujours pas pu l'authentifier. Il s'avère que j'avais besoin d'un '/' de fin dans mon chemin relatif.

c'est à 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
            });
Tony
la source
-6

Alternativement - ne pas utiliser BaseAddressdu tout. Mettez l'URL entière dans GetAsync()

userSteve
la source
33
Ne répond pas du tout à la question.
Archibald
7
Le BaseAddress réduit le bruit. À mes yeux en tout cas. :)
MetalMikester
2
Je dois être en désaccord avec les commentaires négatifs. J'ai passé 2 jours à essayer de comprendre pourquoi mes appels HttpClient fonctionnent sur mon PC Dev mais échouent sur le serveur. Curieusement Powershell fonctionne mais pas .net. J'utilisais .SendAsyc. Ensuite, j'ai découvert que .GetAsyc fonctionnait. Cela m'a conduit sur une autre voie et finalement ici. L'ajout ou la suppression du / entre l'adresse de base et l'URL relative n'a rien fait. J'ai toujours eu des erreurs 404 ... mais quand je n'ai pas défini l'adresse de base et mis le chemin complet dans le parent .. cela a fonctionné! Encore une fois, c'est avec .SendAsync mais il y a eu 2 jours je ne reviendrai jamais!
da_jokker