Почему HttpClient BaseAddress не работает?
Рассмотрим следующий код, где 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 Что здесь происходит?
6 ответов
Оказывается, что из четырех возможных вариантов включения или исключения конечных или ведущих слэшей на 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
,
Эталонное разрешение описывается в RFC 3986 Унифицированный идентификатор ресурса (URI): общий синтаксис. И это именно так, как это должно работать. Чтобы сохранить базовый путь URI, необходимо добавить косую черту в конце базового URI и удалить косую черту в начале относительного URI.
Если базовый URI содержит непустой путь, процедура слияния отбрасывает свою последнюю часть (после последней /
). Соответствующий раздел:
5.2.3. Пути слияния
Вышеупомянутый псевдокод относится к процедуре "слияния" для объединения ссылки относительного пути с путем базового URI. Это достигается следующим образом:
Если базовый URI имеет определенный компонент полномочий и пустой путь, тогда вернуть строку, состоящую из "/", соединенную с путем ссылки; иначе
возвращает строку, состоящую из компонента пути ссылки, добавляемого ко всем, кроме последнего сегмента пути базового URI (т.е. исключая любые символы после крайнего правого "/" в пути базового URI, или исключая весь путь базового URI, если он не содержит символов "/").
Если относительный URI начинается с косой черты, он называется относительным URI абсолютного пути. В этом случае процедура слияния игнорирует все базовые пути URI. Для получения дополнительной информации проверьте 5.2.2. Раздел " Преобразование ссылок ".
если вы используете httpClient.SendAsync() не имеет перегрузки строки, как перегрузки в Get, Post и других методах, специфичных для глаголов.
Но вы можете создать относительный Uri, указав UriKind.Relative в качестве второго параметра
var httpRequestMessage = new HttpRequestMessage
{
Method = httpMethod,
RequestUri = new Uri(relativeRequestUri, UriKind.Relative),
Content = content
};
using var httpClient = HttpClientFactory.CreateClient("XClient");
var response = await httpClient.SendAsync(httpRequestMessage);
var responseText = await response.Content.ReadAsStringAsync();
Я также столкнулся с той же проблемой с. Я решил вообще не использовать, и самым простым решением было бы простое однострочное добавление:
Uri GetUri(string path) => new Uri("http://something.com/api" + path);
Тогда ваш код станет:
Uri GetUri(string path) => new Uri("http://something.com/api" + path);
using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
// Remove BaseAddress completely
// client.BaseAddress = new Uri("http://something.com/api");
var response = await client.GetAsync(GetUri("/resource/7"));
}
Я не исследовал плюсы и минусы использования
BaseAddress
над этим, но для меня это работает безупречно. Надеюсь, это кому-то поможет.
В качестве альтернативы - не используйте BaseAddress
совсем. Поместите весь URL в GetAsync
()
Столкнулся с проблемой с 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
});