为什么HttpClient BaseAddress不起作用?

Tim*_*lds 255 .net c# dotnet-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");
}
Run Code Online (Sandbox Code Playgroud)

我希望这可以执行GET请求http://something.com/api/resource/7.但事实并非如此.

经过一番搜索,我发现这个问题和答案:HttpClient with 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");
}
Run Code Online (Sandbox Code Playgroud)

它仍然无法正常工作.这是文档:HttpClient.BaseAddress这里发生了什么?

Tim*_*lds 605

事实证明,在BaseAddress传递给GetAsync方法的相对URI 上包含或排除尾随或前导斜杠的四种可能的排列中,或者无论其他方法是什么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");
}
Run Code Online (Sandbox Code Playgroud)

即使我回答了我自己的问题,我想我会在这里提供解决方案,因为这种不友好的行为再次没有记录.我的同事和我一天大部分时间都在努力解决最终由这种奇怪问题引起的问题HttpClient.

  • 我可以确认这个奇怪的(和这个修复)在.NET Core中仍然相关.谢谢你减少我拔毛的蒂莫西. (29认同)
  • 我的天啊!谢谢你!八年后,使用带有 .NET 6.0 的 Visual Studio 2022,我们仍在处理这种脑死亡的设计! (26认同)
  • 这不是直接解决原始问题而是相关的.根据Mircosoft,应将HttpClient()的实例分配给静态变量并重用(https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api -from-a-net-client - "每个请求创建一个新的HttpClient实例可以耗尽可用的套接字".所以你应该考虑删除Using(). (11认同)
  • 这是因为在构建请求时没有斜杠,它会删除最后一部分.所以它点击http://something.com/resource/7.如果你将基地址设置为http:// something/com(如果有或没有尾部斜杠,则无关紧要),如果你在api/resource/7的开头加上斜杠也没关系.如果没有尾部斜杠,则基址的最后一部分将被视为文件,并在构建请求时被删除. (8认同)
  • 这个愚蠢的阶级在2018年仍有问题 (7认同)
  • 谢谢.这解决了我在两天的大部分时间里一直在努力解决的问题,在切换到Azure,返回到IIS以及返回到IIS Express之间,最严重的是忽略错位或额外的正斜线.一旦设置在我的'RestClient`的基类中,它几乎是不可见的,根本没有引起注意,我从未在断点处看到完整的URL等. (3认同)
  • 事实上,这显然是不寻常和古怪的。您不应允许基地址处出现尾部斜杠,路径上不允许出现前导斜杠,因为根据标准,路径始终带有斜杠。 (3认同)
  • 基地址上的尾部斜杠在某个时候抓住了我们所有人。http://www.bizcoder.com/2009/02/24/the-mystery-of-the-trailing-slash-and-the-relative-url/ (2认同)
  • 不确定它是否固定,但您不需要 BaseAddress 的尾部斜杠,也不需要在相对路径的开头去掉斜杠 (2认同)
  • @Steve 我无法确认。至少在我的环境中(PCL、Xamarin.Forms、Nancyfx)这解决了我的问题。客户端基础 url 总是说它是正确的,但在 response.RequestMessage.RequestUri 中它仍然是错误的。改变斜线解决了它。 (2认同)
  • 只是一个可怕的实现。他们为什么不解决这个问题? (2认同)
  • 如果您认为这不是正确的答案,请仔细检查您的代码:) (2认同)

Leo*_*lev 48

RFC 3986统一资源标识符(URI):通用语法描述了参考解析.这正是它应该如何运作的.要保留基URI路径,您需要在基URI的末尾添加斜杠,并在相对URI的开头删除斜杠.

如果基URI包含非空路径,则合并过程会丢弃它的最后一部分(最后一个/).相关部分:

5.2.3.合并路径

上面的伪代码指的是用于将相对路径引用与基URI的路径合并的"合并"例程.这完成如下:

  • 如果基URI具有已定义的权限组件和空路径,则返回由引用的路径连接的"/"组成的字符串; 除此以外

  • 返回一个字符串,该字符串包含附加到基URI路径的最后一段之外的所有引用的引用路径组件(即,排除基URI路径中最右侧"/"之后的任何字符,或者排除整个基本URI路径,如果它不包含任何"/"字符).

如果相对URI以斜杠开头,则称为绝对路径相对URI.在这种情况下,合并过程忽略所有基URI路径.有关更多信息,请查看5.2.2.转换参考部分.

  • 很好,但是像 HttpClient 这样的客户端库应该让我们免受像这样深奥的实现细节的影响。 (25认同)
  • 如果答案涵盖了为什么某些东西会以这种方式工作的原因,并带有相关链接和引用,那就太好了。即使这不能直接帮助解决问题,但也有很大帮助。 (9认同)

Ima*_*idi 19

如果您使用的是 httpClient。SendAsync () 没有用于提供相对 Uris 的字符串重载,例如 Get 或其他动词特定方法的重载。

但是您可以通过将UriKind.Relative作为第二个参数来创建相对 Uri

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();
Run Code Online (Sandbox Code Playgroud)

  • 仅当您的基地址仅包含主机地址和域而不包含其他内容时,这似乎才有效。例如,如果您将 `HttpClient` 的 `BaseAddress` 属性设置为 `new Uri("https://mywebservice.com/api/public")` 并创建一个 `HttpRequestMessage` 传递 `RequestUri = new Uri(" users/123456")` 到构造函数,结果请求中使用的 URL 将是 https://mywebservice.com/users/123456。这对我来说很烦人,因为我的所有请求都使用通用的 URL 前缀(例如 https://mywebservice.com/api/public),因此最好能够将其设置为基地址。 (3认同)

kik*_*283 5

我也遇到了同样的问题BaseAddress。我决定根本不使用,BaseAddress最简单的解决方案是简单的一行添加:

Uri GetUri(string path) => new Uri("http://something.com/api" + path);
Run Code Online (Sandbox Code Playgroud)

那么你的代码将变成:

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"));
}
Run Code Online (Sandbox Code Playgroud)

我还没有研究过使用它的利弊BaseAddress,但对我来说这工作完美。希望这对某人有帮助。