Почему 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
            });
Другие вопросы по тегам