繁体   English   中英

HttpClient.BaseAddress的用途是什么,为什么在第一个请求后不能更改它

[英]What is the purpose of HttpClient.BaseAddress and why can't I change it after the first request

因此,我们大多数人可能已经读到,我们应该重用HttpClient实例,而不是使用using和创建新实例。 这意味着我可以在程序中创建一个HttpClient实例, GetAsync使用每个请求的完整uri字符串调用GetAsync 这将我带到HttpClientBaseAddress属性。 考虑以下代码:

HttpClient microsoftClient = new HttpClient() { BaseAddress = new Uri("https://www.microsoft.com/") };
HttpClient stackoverflowClient = new HttpClient() { BaseAddress = new Uri("https://stackoverflow.com/") };

var response = microsoftClient.GetAsync("about").Result;
Console.WriteLine($"I {((response.IsSuccessStatusCode) ? "can" : "cannot")} access microsoft.com/about from the microsoft client");

response = microsoftClient.GetAsync("trademarks").Result;
Console.WriteLine($"I {((response.IsSuccessStatusCode) ? "can" : "cannot")} access microsoft.com/trademarks from the microsoft client");

response = stackoverflowClient.GetAsync("company/about").Result;
Console.WriteLine($"I {((response.IsSuccessStatusCode) ? "can" : "cannot")} access stackoverflow.com/company/about from the stackoverflow client");

response = stackoverflowClient.GetAsync("https://www.microsoft.com/about").Result;
Console.WriteLine($"I {((response.IsSuccessStatusCode) ? "can" : "cannot")} access microsoft.com/about from the stackoverflow client");

microsoftClient.BaseAddress = new Uri("https://stackoverflow.com");
response = microsoftClient.GetAsync("company/about").Result;
Console.WriteLine($"I {((response.IsSuccessStatusCode) ? "can" : "cannot")} access stackoverflow.com/company/about from the microsoft client, after changing the BaseAddress");

直到最后一个块,即使将客户端与stackoverflow BaseAddress一起使用来访问Microsoft,此代码也可以正常运行。 但是,当重新分配BaseAddress ,此代码在最后一个块的开头引发InvalidOperationException ,说明

'This instance has already started one or more requests. Properties can only be modified before sending the first request.'

这使我想到以下问题:

  1. 完全使用BaseAddress什么好处? 我可以总是在我的GetAsync调用中使用完整的地址。 是否只是为了方便/高效而不必构建完整的请求字符串? 我的猜测是,它将仅在内部创建一个ServicePoint ,如本博客文章第一段所述(或类似的帖子已经很老了)。
  2. 在内部发生什么,我们在发送第一个请求后无法更改HttpClient的属性,尤其是BaseAddress 如果实际上使用此属性会带来好处,这似乎非常不便。

对于(1),一个常见的用例是与一台服务器交互的客户端。 也许这是此客户端使用的后端API。 确切的详细信息将存储在客户端在启动过程中读取的配置文件中。

我们可以直接访问config来填充代码,也可以将从config中读取的字符串注入需要构造完整URL的每个位置。 或者我们可以只配置要放入依赖注入容器中的HttpClient的BaseAddress ,并让使用位置注入该对象。 对我来说,这是一个有些期望的用例。

对于(2),我认为没有技术限制。 我认为这更多的是要使人们摆脱自己的命运。 由于设置BaseAddress并导致实际的请求通过例如GetAsync是单独的操作,因此两个单独的代码段同时执行此类操作将是不安全的-您很容易出现竞争。 因此,如果一开始不允许此类HttpClientHttpClient容易HttpClient可能共享单个HttpClient实例的多线程程序。

2个目的:

  1. 方便。 如果要从单个主机上调用多个终结点,并且将基地址和终结点段作为单独的字符串进行管理(非常常见),则可以帮助您避免在每次调用时进行难看的字符串连接。

  2. 鼓励最佳做法。 尽管通过GetAsync等进行调用是线程安全的,但HttpClient除了BaseAddress之外还具有其他一些属性,例如DefaultRequestHeaders 通常,您希望对相同主机的调用它们相同,但对不同主机的调用则不相同。 因此, 每个主机被调用的HttpClient实例实际上是一个很好的做法。 除非您要呼叫数千个不同的主机,否则无需担心此处臭名昭著的套接字耗尽问题 (即使您使用的是单例,底层网络堆栈仍然需要为每个主机打开一个不同的套接字。)

那么,为什么在HttpClient调用上指定完整地址甚至没有用? 再次,方便。 该地址可能来自外部来源或用户输入,因此您无需为了使用它而将其分成多个部分。 但是在这种情况下,您必须确保线程安全,并且应该完全避免那些非线程安全属性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM