Я выполняю простой запрос GET к веб-API ASP.NET Core, который я создал в Xamarin.Forms 4.8. Для этого я использую следующий код:

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 (а также удаления и замены экземпляра после изменения его конфигурации). Таким образом, после настройки один и тот же экземпляр HttpClient будет возвращаться всякий раз, когда используется service.Client. Код выглядит примерно так:

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();
    }
}

Проблема в строке, в которой я пытаюсь прочитать HttpContent из HttpResponseMessage. Каждый раз, когда я его вызываю, я получаю сообщение System.ObjectDisposedException: «Нет доступа к удаленному объекту. Имя объекта: 'System.Net.Http.StreamContent'. '.

Я уже пробовал установить для параметра disposeHandler HttpClient значение true и false, так как я видел в Интернете людей, предлагающих, что это решит проблему, но пока мне не повезло.

0
Christoph Mett 9 Дек 2020 в 19:39

1 ответ

Лучший ответ

Проблема не в утилизации. Здесь есть две проблемы, одна из которых вызывает другую:

  1. Исключения используются для управления потоком. Вместо того, чтобы выдавать ошибку, вы можете проверить код состояния ответа
  2. До .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);
}

Бросок также запутывает ответную фразу, что значительно усложняет устранение неполадок.

1
Panagiotis Kanavos 9 Дек 2020 в 17:06