You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 07:23:21