Por que o HttpClient BaseAddress não está funcionando?

299

Considere o código a seguir, onde BaseAddressdefine um caminho URI parcial.

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

Eu espero que isso execute um GET solicitação para http://something.com/api/resource/7. Mas isso não acontece.

Após algumas pesquisas, acho esta pergunta e resposta: HttpClient with BaseAddress . A sugestão é colocar /no final doBaseAddress .

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

Ainda não funciona. Aqui está a documentação: HttpClient.BaseAddress O que está acontecendo aqui?

Timothy Shields
fonte
Possível duplicata do HttpClient com BaseAddress
George Lanetz
@ ReverseеоргийЛанец A duplicata reversa já foi proposta. Eu escrevi essa pergunta especificamente porque essa outra pergunta não foi escrita de uma maneira que era muito detectável por pessoas com o mesmo problema, e eu escrevi a resposta aqui porque a resposta ali deixou um ponto importante.
Timothy Shields
mas esta pergunta é feita mais tarde
George Lanetz
2
@ ГеоргийЛанец Não é assim que funciona. Geralmente, a pergunta mais "canônica" é aquela que recebe as duplicatas apontando para ela. Essa outra pergunta era sobre um único problema que o usuário estava tendo, em vez de ler como um FAQ.
Timothy Shields
2
@ ГеоргийЛанец Observe também que refiro a outra pergunta nesta pergunta e explico por que a outra pergunta e resposta são insuficientes para resolver o problema.
Timothy Shields

Respostas:

719

Acontece que, das quatro permutações possíveis de incluir ou excluir barras à direita ou à esquerda no BaseAddressURI relativo e passado para o GetAsyncmétodo - ou qualquer outro método de HttpClient- apenas uma permutação funciona. Você deve colocar uma barra no final do BaseAddresse não deve colocar uma barra no início do seu URI relativo, como no exemplo a seguir.

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

Mesmo tendo respondido minha própria pergunta, achei que contribuiria com a solução aqui, pois, novamente, esse comportamento hostil não é documentado. Meu colega e eu passamos a maior parte do dia tentando resolver um problema que foi causado por essa estranheza HttpClient.

Timothy Shields
fonte
4
Obrigado. Isso resolveu um problema com o qual estou lutando há mais de dois dias, entre mudar para o Azure, voltar para o IIS e voltar para o IIS Express, que mais grosseiramente ignora barras invertidas ou extraviadas. Uma vez definido na classe base do meu RestClient, era quase invisível e não
chamava a
43
Posso confirmar que essa estranheza (e essa correção) ainda é relevante no .NET Core. Obrigado por reduzir meu Timothy puxador de cabelo.
Nate Barbettini 29/03/19
8
Isso ocorre porque, sem a barra à direita, quando cria solicitações, ela solta a última parte. Então, atinge algo.com/resource/7 . Se você definir o endereço base como algo / com (não importa se com ou sem barra), também não importa se você colocar barra no início de api / resource / 7. Sem a barra à direita, a última parte do endereço base é tratada como um arquivo e descartada quando a solicitação é feita em massa.
Piotr Perak
12
Isso não está diretamente relacionado à pergunta original, mas relacionado. Por Mircosoft, uma instância de HttpClient () deve ser atribuída a uma variável estática e reutilizada ( docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/… - Creating a new HttpClient instance per request can exhaust the available sockets). Portanto, você deve considerar a remoção de Using ().
sanmcp
6
Apenas uma implementação horrível. Por que eles não consertam isso?
timmkrause
55

A resolução de referência é descrita pelo URI (RFC 3986 Uniform Resource Identifier): Sintaxe genérica . E é exatamente assim que deveria funcionar. Para preservar o caminho do URI base, você precisa adicionar uma barra no final do URI base e remover a barra no início do URI relativo.

Se o URI base contiver um caminho não vazio, o procedimento de mesclagem descartará a última parte (depois da última /). Seção relevante :

5.2.3 Mesclar caminhos

O pseudocódigo acima refere-se a uma rotina de "mesclagem" para mesclar uma referência de caminho relativo com o caminho do URI base. Isso é feito da seguinte maneira:

  • Se o URI base tiver um componente de autoridade definido e um caminho vazio, retorne uma sequência que consiste em "/" concatenada com o caminho da referência; de outra forma

  • retorne uma string que consiste no componente de caminho da referência anexado a todos, exceto o último segmento do caminho do URI base (ou seja, excluindo caracteres após o "/" mais à direita no caminho URI base ou excluindo todo o caminho URI base, se não contém caracteres "/").

Se o URI relativo começar com uma barra, ele será chamado de URI relativo do caminho absoluto. Nesse caso, o procedimento de mesclagem ignora todo o caminho URI base. Para mais informações, consulte 5.2.2. Seção Transformar referências .

Leonid Vasilev
fonte
3
Bibliotecas finas, mas clientes como o HttpClient devem nos proteger de detalhes esotéricos de implementação como este.
Jamie Ide
-1

Ocorreu um problema com o HTTPClient, mesmo com as sugestões ainda não foi possível autenticar. Acontece que eu precisava de um '/' à direita no meu caminho relativo.

ie

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
fonte
-6

Como alternativa - não use BaseAddressnada. Coloque o URL inteiro em GetAsync()

userSteve
fonte
33
Não responde a pergunta de forma alguma.
Archibald
7
O BaseAddress reduz o ruído. Aos meus olhos de qualquer maneira. :)
MetalMikester
2
Vou ter que discordar dos comentários negativos. Passei 2 dias tentando descobrir por que minhas chamadas HttpClient funcionam no meu PC Dev, mas quebram no servidor. Estranhamente, o Powershell funciona, mas o .net não. Eu estava usando .SendAsyc. Então eu descobri que .GetAsyc funcionou. Isso me levou por um caminho diferente e, eventualmente, aqui. Adicionar ou remover o / entre o Endereço base e o URL relativo não fez nada. Ainda tenho 404 erros .... no entanto, quando eu não defini o endereço base e coloquei o caminho inteiro no relativo .. funcionou! Novamente, isso é com .SendAsync, mas houve 2 dias que eu nunca voltarei!
da_jokker 23/01