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

.NET MAUI中HttpClient带请求体POST时出现Stream已关闭错误

.NET MAUI Android POST请求抛出ObjectDisposedException: Cannot access a closed Stream

问题详情

在.NET MAUI应用中,Android设备(含模拟器)上发起带请求体的POST请求时触发以下错误,Windows平台测试完全正常:

System.ObjectDisposedException: Cannot access a closed Stream.
   at System.IO.MemoryStream.EnsureNotClosed()
   at System.IO.MemoryStream.CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
   at Xamarin.Android.Net.AndroidMessageHandler.WriteRequestContentToOutput(HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 458
   at Xamarin.Android.Net.AndroidMessageHandler.DoProcessRequest(HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken, RequestRedirectionState redirectState) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 533
   at Xamarin.Android.Net.AndroidMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 375
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at MyApp.Services.HttpService.PostAsync(String url, String body, String token, Dictionary`2 headers) in C:\Users\user\Documents\temp\MyApp\MyApp\Services\HttpService.cs:line 38

当前实现代码:

public async Task<string> PostAsync(string url, string body = "", string? token = "", Dictionary<string, string> headers = null)
{
    HttpClient client = new();
    try
    {
        var request = new HttpRequestMessage(HttpMethod.Post, url)
        {
            Content = new StringContent("", Encoding.UTF8, "application/json"),
        };

        if (headers != null)
        {
            foreach (var header in headers)
            {
                request.Headers.Add(header.Key, header.Value);
            }
        }

        if (token != "") request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);


        var result = await client.PostAsync(url, new StringContent("", Encoding.UTF8, "application/json"));
        //var result = await client.SendAsync(request);

        var test = await result.Content.ReadAsStringAsync();

        if (result.StatusCode != HttpStatusCode.OK)
        {
            throw new Exception("Något gick fel");
        }

        return await result.Content.ReadAsStringAsync();
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

问题根源

  1. 重复读取HttpContent流:代码中两次调用await result.Content.ReadAsStringAsync(),HttpContent的流在第一次读取后会被自动关闭,第二次读取时直接触发流已释放的异常。Windows平台的HttpClient Handler做了兼容处理,但Android的AndroidMessageHandler没有。
  2. 逻辑冗余与参数浪费:创建了HttpRequestMessage但未实际使用,反而调用PostAsync重新创建空的StringContent,传入的body参数完全被忽略。
  3. HttpClient未复用:每次请求创建新的HttpClient实例,会导致连接池资源耗尽,同时增加不必要的性能开销。

修复方案

修改后的代码:

// 全局复用HttpClient,推荐在依赖注入中注册为单例
private static readonly HttpClient _httpClient = new HttpClient();

public async Task<string> PostAsync(string url, string body = "", string? token = "", Dictionary<string, string> headers = null)
{
    try
    {
        using var request = new HttpRequestMessage(HttpMethod.Post, url);
        
        // 正确使用传入的body参数构建请求体
        request.Content = new StringContent(body, Encoding.UTF8, "application/json");

        if (headers != null)
        {
            foreach (var header in headers)
            {
                // 区分请求头与内容头,避免添加失败
                if (!request.Headers.TryAddWithoutValidation(header.Key, header.Value))
                {
                    request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
                }
            }
        }

        if (!string.IsNullOrEmpty(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }

        var result = await _httpClient.SendAsync(request);

        // 仅读取一次响应内容,复用变量避免流重复读取
        var responseContent = await result.Content.ReadAsStringAsync();

        if (!result.IsSuccessStatusCode)
        {
            throw new Exception($"请求失败: {result.StatusCode}, 响应内容: {responseContent}");
        }

        return responseContent;
    }
    catch (Exception ex)
    {
        // 保留原始堆栈信息,避免throw ex丢失调用链
        throw;
    }
}

关键修复点:

  • 复用HttpClient实例,遵循.NET HttpClient最佳实践
  • 仅读取一次响应内容,存储到变量中复用
  • 正确使用传入的body参数构建请求体
  • 区分请求头与内容头的添加逻辑
  • 修复错误处理方式,保留原始异常堆栈信息

内容的提问来源于stack exchange,提问作者Hickori

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 23:45:16