调用不同BaseAddress时HttpClient的最佳实践及异常疑问
核心结论:绝对不要复用同一个HttpClient实例修改BaseAddress服务多个地址
你的做法存在严重线程安全隐患:HttpClient本身是线程安全的,但BaseAddress是可变属性。当多个线程同时使用该实例时,BaseAddress的修改会导致请求被发送到错误地址——比如线程A刚设置地址1准备发请求,线程B立刻将地址改成2,线程A的请求就会错发到地址2,完全不符合预期。
关于你遇到的异常
你捕获的Unexpected character encountered while parsing value: <. Path '', line 0, position 0.异常,几乎可以肯定是上述问题导致的:请求被发送到错误地址,返回了HTML内容(比如404页面、跳转页面),而代码预期解析JSON,因此抛出解析错误。<正是HTML标签的起始符,印证了响应并非JSON格式。
正确的最佳实践
1. 使用HttpClientFactory创建命名客户端(推荐)
在依赖注入配置阶段,为每个BaseAddress创建独立的命名客户端,预先配置好BaseAddress:
// 服务配置时 services.AddHttpClient("mfEODListApi", client => { client.BaseAddress = new Uri("http://UniqueAddress_1"); // 可额外添加超时、请求头等配置 }); services.AddHttpClient("MFListApi", client => { client.BaseAddress = new Uri("http://UniqueAddress_2"); });
使用时直接获取对应命名的HttpClient实例:
_mfEODListApi = new RestEase.RestClient(_httpClientFactory.CreateClient("mfEODListApi")); _MFListApi = new RestEase.RestClient(_httpClientFactory.CreateClient("MFListApi"));
这种方式由HttpClientFactory管理HttpClient生命周期,避免socket资源耗尽,同时每个客户端配置完全隔离,不会互相干扰。
2. 为每个BaseAddress创建独立的RestEase客户端
RestEase支持直接通过地址初始化,无需手动管理HttpClient:
_mfEODListApi = new RestEase.RestClient("http://UniqueAddress_1"); _MFListApi = new RestEase.RestClient("http://UniqueAddress_2");
RestEase会为每个实例创建并管理对应的HttpClient(默认单例),既避免地址冲突,代码也更简洁。
3. 手动管理独立HttpClient实例(不推荐,仅特殊场景使用)
若必须手动管理,需确保每个BaseAddress对应独立的HttpClient实例,并将其设为单例或通过注入保持稳定生命周期:
// 为地址1创建独立实例 var mfEodHttpClient = httpClientFactory.CreateClient(); mfEodHttpClient.BaseAddress = new Uri("http://UniqueAddress_1"); _mfEODListApi = new RestEase.RestClient(mfEodHttpClient); // 为地址2创建独立实例 var mfListHttpClient = httpClientFactory.CreateClient(); mfListHttpClient.BaseAddress = new Uri("http://UniqueAddress_2"); _MFListApi = new RestEase.RestClient(mfListHttpClient);
注意不要频繁创建HttpClient实例,否则会导致系统socket资源耗尽。
内容的提问来源于stack exchange,提问作者dddave

