如何重构HTTP服务代码避免重复?解决已释放对象异常问题
一、IHttpService代码重构优化方案
问题分析
现有代码中,泛型Post<T>与非泛型Post方法、两个sendRequest方法的核心逻辑(超时控制、请求发送、错误处理)高度重复,需要通过重构消除冗余。
可行方案与最优选择
1. 直接调用泛型实现非泛型(可行但非最优)
可以让非泛型Post调用Post<object>并忽略返回值,代码如下:
public async Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default) { await Post<object>(uri, content, timeOut, token); }
但这种方式会额外执行一次object类型的反序列化,存在不必要的性能开销,因此不是最优方案。
2. 抽取公共核心方法(最优方案)
将请求发送、超时控制、错误检查等公共逻辑抽离为独立方法,让泛型和非泛型方法各自处理结果部分,彻底消除重复代码:
// 公共核心方法:处理请求发送与错误校验 private async Task<HttpResponseMessage> SendRequestCore(HttpRequestMessage request, int timeOut, CancellationToken token) { using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut)); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token); var response = await _httpClient.SendAsync(request, linkedCts.Token); if (!response.IsSuccessStatusCode) { var error = await response.Content.ReadAsStringAsync(token); throw new Exception(error); } return response; } // 非泛型Post方法:仅调用核心方法,不处理返回内容 public async Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default) { var request = new HttpRequestMessage(HttpMethod.Post, uri); request.Content = content; await SendRequestCore(request, timeOut, token); } // 泛型Post方法:调用核心方法后反序列化返回结果 public async Task<T> Post<T>(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default) { var request = new HttpRequestMessage(HttpMethod.Post, uri); request.Content = content; var response = await SendRequestCore(request, timeOut, token); return (await response.Content.ReadFromJsonAsync<T>(cancellationToken: token))!; }
该方案既避免了代码重复,又消除了不必要的序列化操作,在性能与可维护性上表现最优。
二、ObjectDisposedException异常解决
异常原因
重构后的sendRequest方法中,HttpResponseMessage被using关键字包裹:
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
当方法返回response.Content时,using块已执行完毕,HttpResponseMessage会被自动释放,其关联的HttpContent也会被一并销毁,导致外部调用ReadFromJsonAsync时触发Cannot access a disposed object异常。
解决方法
方案1:返回HttpResponseMessage,由调用方管理生命周期
修改sendRequest返回HttpResponseMessage,去掉response的using声明,让调用方用using包裹并管理其生命周期:
private async Task<HttpResponseMessage> sendRequest(HttpRequestMessage request, int timeOut = 100, CancellationToken token = default) { try { using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut)); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token); var response = await _httpClient.SendAsync(request, linkedCts.Token); if (!response.IsSuccessStatusCode) { var error = await response.Content.ReadAsStringAsync(token); throw new Exception(error); } return response; } catch (AccessTokenNotAvailableException) { _navigationManager.NavigateToLogout(StaticStringHelper.LogoutPatch); throw; } } // Get方法调用时管理response生命周期 public async Task<T> Get<T>(string uri, int timeOut = 100, CancellationToken token = default) { var request = new HttpRequestMessage(HttpMethod.Get, uri); using var response = await sendRequest(request, timeOut, token); return await response.Content.ReadFromJsonAsync<T>(cancellationToken: token); }
方案2:在sendRequest内完成反序列化(泛型核心思路)
如果不想暴露HttpResponseMessage,可以将sendRequest改为泛型方法,直接返回反序列化后的结果,避免返回HttpContent:
private async Task<T?> SendRequest<T>(HttpRequestMessage request, int timeOut, CancellationToken token) { using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut)); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token); using var response = await _httpClient.SendAsync(request, linkedCts.Token); if (!response.IsSuccessStatusCode) { var error = await response.Content.ReadAsStringAsync(token); throw new Exception(error); } return typeof(T) == typeof(object) ? default : await response.Content.ReadFromJsonAsync<T>(cancellationToken: token); } // 非泛型Post调用泛型方法,传入object忽略返回 public async Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default) { var request = new HttpRequestMessage(HttpMethod.Post, uri); request.Content = content; await SendRequest<object>(request, timeOut, token); } // 泛型Post直接调用 public async Task<T> Post<T>(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default) { var request = new HttpRequestMessage(HttpMethod.Post, uri); request.Content = content; return (await SendRequest<T>(request, timeOut, token))!; } // Get方法调用泛型版本 public async Task<T> Get<T>(string uri, int timeOut = 100, CancellationToken token = default) { var request = new HttpRequestMessage(HttpMethod.Get, uri); return (await SendRequest<T>(request, timeOut, token))!; }
内容的提问来源于stack exchange,提问作者Dorian
相关产品推荐
相关产品推荐

