HttpContent 在 try 和 catch 之间得到处理

Posted

技术标签:

【中文标题】HttpContent 在 try 和 catch 之间得到处理【英文标题】:HttpContent gets disposed between try and catch 【发布时间】:2020-12-09 16:39:31 【问题描述】:

我正在对我在 Xamarin.Forms 4.8 中创建的 ASP.NET Core Web API 执行一个简单的 GET 请求。为此,我使用以下代码:

public async Task<Result<bool>> GetSomeResult()

    var client = service.Client;

    HttpResponseMessage response = null;

    try
    
        response = await client.GetAsync(new UriHelper(endpoint, "someEndpoint")).ConfigureAwait(false);

        response.EnsureSuccessStatusCode(); // will throw a exception on a non-success status code

        return await response.Content.ReadAsAsync<bool>().ConfigureAwait(false);
    
    catch (Exception ex)
    
        if (response?.StatusCode == HttpStatusCode.InternalServerError)
        
            // !!! The error occurs in the next line !!!
            SomeErrorClass id = await response.Content.ReadAsAsync<SomeErrorClass>().ConfigureAwait(false);
            return new Result<bool>(SomeErrorClass.ToString());
        

        return new Result<bool>(ex);
    

service 是一个单例,它被注入到周围类的构造函数中(通过 DryIoc)。这个service 是一个HttpClient 实例的包装器,它只是提供了一个配置HttpClient 的工具(以及在其配置发生变化时处理和替换实例)。因此,在配置之后,无论何时使用 service.Client,都会返回相同的 HttpClient 实例。代码是这样的:

public class ServiceConnection

    private const string Localhost = "https://127.0.0.1";

    private readonly PreferenceService preferences;
    private readonly IHttpClientHandlerProvider handlerProvider;

    public HttpClient Client  get; private set; 

    public ServiceConnection(PreferenceService preferences, IHttpClientHandlerProvider handlerProvider)
    
        this.preferences = preferences;
        this.handlerProvider = handlerProvider;

        Client = CreateClient();
    

    private HttpClient CreateClient()
    
        var handler = new TimeoutHandler()
        
            DefaultTimeout = TimeSpan.FromSeconds(10),
            InnerHandler = handlerProvider?.GetHandler(opt =>
            
                opt.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
                opt.UseDefaultCredentials = true;
                opt.AllowAutoRedirect = true;
            )
        ;

        Uri.TryCreate($"preferences.Server ?? Localhost/api/", UriKind.Absolute, out var baseAddress);

        var client = new HttpClient(handler, true)
        
            BaseAddress = baseAddress,
            Timeout = TimeSpan.FromSeconds(20),
        ;

        client.DefaultRequestHeaders.Add(CustomHttpHeaders.DeviceId, preferences.UUID);

        return client;
    

    public void RefreshConnection()
    
        Client?.Dispose();
        Client = CreateClient();
    

问题是我尝试从HttpResponseMessage 读取HttpContent 的行。每当我调用它时,我都会收到 System.ObjectDisposedExceptionsaying '无法访问已处置的对象。 对象名称:'System.Net.Http.StreamContent'。'。

我已经尝试将HttpClientdisposeHandler 参数设置为truefalse,因为我在互联网上看到一些人建议这样可以解决问题,但我没有运气所以远。

【问题讨论】:

内容还是?您的代码已经从网络流中读取了所有内容,因此无法再次从中读取。 NetworkStream 由 服务器 控制,不能只是重启 尝试两次读取和反序列化相同的内容,作为两个不同的类,即使有可能也是浪费。内容只需阅读一次。之后,可以使用您想要的任何反序列化器对实际内容进行反序列化 完全不扔会更好(而且更快)。不要调用EnsureSuccessStatusCode(),而是检查响应的状态。现在您正在使用异常进行流控制,此时 if 可以工作 从docs"在.NET Framework和.NET Core 2.2及更早版本中,如果Content不为null,该方法也会调用Dispose释放托管和非托管资源。 " 顺便说一句EnsureStatusCode disposes when it throws。所以是的,例外是原因 【参考方案1】:

问题不在于处置。这里有两个问题,一个导致另一个:

    异常用于流控制。您可以检查响应的状态代码,而不是抛出失败 在 .NET Core 3 之前,EnsureStatusCode 关闭流。

根本原因是使用异常进行流控制。

这可以通过解决第一个问题来解决,这也将大大提高性能很多。抛出异常是昂贵的,比if 贵几个数量级。速度快 100-1000 倍:

if (response.IsSuccessStatusCode)

    var value=await response.Content.ReadAsAsync<bool>();
    return value; //Should this be `new Result(value) ??

else if (response.StatusCode == HttpStatusCode.InternalServerError)

    var id = await response.Content.ReadAsAsync<SomeErrorClass>();
    return new Result<bool>(SomeErrorClass.ToString());

else

    var reason=$"response.StatusCode:response.ReasonPhrase";
    return new Result<bool>(reason);

投掷也会混淆响应短语,这会使故障排除变得更加困难

【讨论】:

可悲的是,似乎没有关于 .NET Standard/Xamarin.Forms/Xamarin.android(我在这里使用)的消息,但我只是假设处置也在那里完成。跨度>

以上是关于HttpContent 在 try 和 catch 之间得到处理的主要内容,如果未能解决你的问题,请参考以下文章

try和catch

try/catch 和 MFC TRY/CATCH 有啥区别?

JSP代码 我不知道怎么放入try和catch

js - try..catch详解

在java语言中try-catch的catch中能否再加try--catch?

js中try和catch的用法