调用ReadAsStringAsync时触发System.ObjectDisposedException问题
问题:调用HttpResponseMessage.ReadAsStringAsync()时抛出ObjectDisposedException异常
现象
调用HttpClient.GetAsync()获取响应后,读取Content属性内容时始终抛出System.ObjectDisposedException,预期应返回JSON字符串。即使将HttpClient构造函数的disposeHandler设为false也无法解决该问题。
补充说明
调用Nextcloud API时,返回200响应时一切正常,但返回401 Unauthorized时触发该异常。已编写NetworkException()方法处理异常并抛出自定义异常,但在该方法中调用ReadAsStringAsync()时同样报错。
相关代码
internal async Task<T> HandleMessage<T>(HttpResponseMessage message) { string jsonContent = await message.Content.ReadAsStringAsync(); // 报错行 return JsonConvert.DeserializeObject<T>(jsonContent); }
internal async Task<T> GetRequest<T>(string route) { var response = await _httpClient.GetAsync(new Uri(_baseUrl, route)); try { response.EnsureSuccessStatusCode(); } catch (HttpRequestException eax) // HTTP错误触发 { await NetworkException(route, response, eax); } return await HandleMessage<T>(response); }
internal async Task NetworkException(string route, HttpResponseMessage message, HttpRequestException innerEx) { var ms = await message.Content.ReadAsStringAsync(); // 报错行 throw new SiliconNetworkException($"Unknown Network Error occured trying to connect to {route}. See Inner Exception for more information", innerEx, JsonConvert.DeserializeObject<SiliconOCSResponse<List<string>>>(ms) ); }
堆栈跟踪
at System.Net.Http.HttpContent.CheckDisposed() at System.Net.Http.HttpContent.ReadAsStringAsync() at SiliconCloud.Network.SiliconNetwork.<NetworkException>d__22.MoveNext() in C:\Users\Weasel\source\repos\Silicon\SiliconCloud\Network\SiliconNetwork.cs:line 233 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at SiliconCloud.Network.SiliconNetwork.<GetRequest>d__16`1.MoveNext() in C:\Users\Weasel\source\repos\Silicon\SiliconCloud\Network\SiliconNetwork.cs:line 174 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at SiliconCloud.UserManagement.UserProvisioning.SiliconUserInstruction.<GetAllUsers>d__17.MoveNext() in C:\Users\Weasel\source\repos\Silicon\SiliconCloud\UserManagement\UserProvisioning\SiliconUserInstruction.cs:line 278
解决方案
问题根源
HttpContent对象只能被读取一次,读取完成后会被标记为已消耗/释放。当前代码中,401异常触发后,NetworkException方法会先读取一次响应内容,而catch块执行完毕后,代码还会继续执行return await HandleMessage<T>(response);,再次尝试读取已被消耗的HttpContent,从而抛出ObjectDisposedException。
修复方案1:终止异常后的后续流程
在catch块处理完异常后,添加throw语句确保后续代码不再执行,避免重复读取响应内容:
internal async Task<T> GetRequest<T>(string route) { var response = await _httpClient.GetAsync(new Uri(_baseUrl, route)); try { response.EnsureSuccessStatusCode(); } catch (HttpRequestException eax) { await NetworkException(route, response, eax); // 强制抛出,确保后续代码不执行 throw; } return await HandleMessage<T>(response); }
修复方案2:提前缓存响应内容
在判断响应状态码之前,先一次性读取响应内容并缓存,后续无论成功还是失败都复用该内容,彻底避免重复读取的问题:
internal async Task<T> GetRequest<T>(string route) { var response = await _httpClient.GetAsync(new Uri(_baseUrl, route)); // 提前读取响应内容并缓存 string content = await response.Content.ReadAsStringAsync(); try { response.EnsureSuccessStatusCode(); return JsonConvert.DeserializeObject<T>(content); } catch (HttpRequestException eax) { throw new SiliconNetworkException($"调用 {route} 时发生未知网络错误,详见内部异常", eax, JsonConvert.DeserializeObject<SiliconOCSResponse<List<string>>>(content)); } }
这个方案还能简化代码结构,不需要单独的HandleMessage和NetworkException方法,减少冗余。
内容的提问来源于stack exchange,提问作者exafile
相关产品推荐
相关产品推荐

