Чому HttpClient BaseAddress не працює?


299

Розглянемо наступний код, де BaseAddressвизначається частковий шлях URI.

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

Я очікую, що це виконає GETзапит до http://something.com/api/resource/7. Але це не так.

Після деякого пошуку я знайду це запитання та відповідь: HttpClient з BaseAddress . Пропозиція розмістити /в кінці 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");
}

Це все ще не працює. Ось документація: HttpClient.BaseAddress Що тут відбувається?


Можливий дублікат HttpClient з BaseAddress
Джордж Ланець

@ ГеоргийЛанец Зворотний дублікат вже запропонований. Я написав це питання спеціально тому, що це інше запитання було написане не таким чином, який був дуже відкритим для людей з тією ж проблемою, і я написав відповідь тут, оскільки відповідь там залишила важливий момент.
Тимофій Шилдс

але це питання задається пізніше
Джордж Ланець

2
@ ГеоргийЛанец Це не так, як це працює. Зазвичай найбільш "канонічне" питання - це те, що отримує дублікати, що вказують на нього. Це інше питання стосувалося однієї проблеми, яка виникала у користувача, а не читати як FAQ.
Тимофій Шилдс

2
@ ГеоргийЛанец Також зауважую, що я посилаюся на це інше питання в цьому запитанні, і я пояснюю, чому інше питання та відповідь недостатня для вирішення проблеми.
Тимофій Шилдс

Відповіді:


719

Виявляється, з чотирьох можливих перестановок включення або виключення кінцевих або ведучих косих переходів на BaseAddressта відносному URI, переданому GetAsyncметоду - або будь-який інший метод HttpClient- працює лише одна перестановка. Ви повинні розміщувати косу рису в кінці BaseAddress, і не слід розміщувати косу рису на початку вашого відносного URI, як у наступному прикладі.

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

Незважаючи на те, що я відповів на власне запитання, я подумав, що буду сприяти вирішенню тут, оскільки, знову ж таки, ця недоброзичлива поведінка є недокументованою. Ми з колегою провели більшу частину дня, намагаючись виправити проблему, яка в кінцевому рахунку була викликана цією дивацтвом HttpClient.


4
Дякую. Це вирішило проблему, з якою я боровся більшість двох днів зараз, між переключенням на Azure, назад до IIS і назад до IIS Express, який найбільш грубо ігнорує пропущені або додаткові передні коси. Після встановлення в базовий клас мого RestClientвін був майже непомітний і на нього взагалі не було уваги, і я ніколи не бачив повного URL-адреси на своїх
точках

43
Я можу підтвердити, що ця дивацтво (і це виправлення) все ще актуально в .NET Core. Дякую, що Тимофій зменшив мою зачіску.
Нейт Барбеттіні

8
Це тому, що без останньої косої риси під час створення запитів вона скидає останню частину. Тож він потрапляє на щось.com/ resource/7 . Якщо ви встановите базову адресу як щось / com (неважливо, з чи без косої риски), це також не має значення, якщо ви ставите косу рису на початку api / resource / 7. Без останньої косої риски остання частина базової адреси обробляється як файл і видаляється при складанні запиту.
Piotr Perak

12
Це не стосується безпосередньо початкового питання, але пов'язане з цим. За Mircosoft екземпляр HttpClient () повинен бути призначений статичній змінній та повторно використаний ( docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/… - Creating a new HttpClient instance per request can exhaust the available sockets). Тож варто розглянути питання про видалення за допомогою ().
sanmcp

6
Просто жахлива реалізація. Чому вони цього не виправлять?
timmkrause

55

Довідкова роздільна здатність описується RFC 3986 Уніфікований ідентифікатор ресурсу (URI): Загальний синтаксис . І саме так воно і повинно працювати. Щоб зберегти базовий шлях URI, вам потрібно додати косу рису в кінці базового URI та видалити косу рису на початку відносного URI.

Якщо базовий URI містить не порожній шлях, процедура злиття відкидає його останню частину (після останньої /). Відповідний розділ :

5.2.3. Об’єднати шляхи

Вищевказаний псевдокод посилається на процедуру "злиття" для об'єднання посилання відносного шляху з контуром базового URI. Це здійснюється наступним чином:

  • Якщо базовий URI має визначений компонент повноважень та порожній шлях, то повертайте рядок, що складається з "/", об'єднаного з контуром посилання; інакше

  • повернути рядок, що складається з компонента шляху посилання, доданого до всіх, крім останнього сегмента шляху базового URI (тобто, виключаючи будь-які символи після найбільш правого "/" в базовому шляху URI, або виключаючи весь базовий шлях URI, якщо він не містить символів "/").

Якщо відносний URI починається з косої риски, це називається URI абсолютного шляху. У цьому випадку процедура злиття ігнорує весь базовий шлях URI. Для отримання додаткової інформації перевірте 5.2.2. Розділ " Трансформація ".


3
Прекрасні, але клієнтські бібліотеки, такі як HttpClient, повинні захищати нас від таких езотеричних деталей реалізації, як ця.
Джеймі Іде

-1

Виникла проблема з HTTPClient, навіть з пропозиціями все-таки не вдалося зробити його автентифікаційним. Виявляється, мені потрібен був проміжок '/' у моєму відносному шляху.

тобто

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

Або ж взагалі не використовуйте BaseAddress. Помістіть всю URL-адресу GetAsync()


33
Не відповідає ні на що питання.
Арчібальд

7
BaseAddress зменшує шум. На мої очі все одно. :)
MetalMikester

2
Мені доведеться не погодитися з негативними коментарями. Я провів 2 дні, намагаючись з’ясувати, чому мої дзвінки HttpClient працюють на моєму комп'ютері Dev, але не працюють на сервері. Як не дивно Powershell працює, але .net цього не робить. Я використовував .SendAsyc. Потім я виявив, що .GetAsyc спрацював. Це веде мене по іншому шляху і, врешті-решт, сюди. Додавання або видалення / між базовою адресою та відносною URL-адресою нічого не робило. У мене все-таки 404 помилки .... однак, коли я не встановив базову адресу, і увесь шлях поставив у відносний .. він працював! Знову ж таки, це з .SendAsync, але 2 дні я ніколи не повернусь!
da_jokker
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.