C # асинхронный тупик с HttpClient при вызове из события Xamarin, почему? - PullRequest
0 голосов
/ 24 октября 2018

У меня возникла проблема взаимоблокировки с вызовами HttpClient.Я предполагаю, что тупик - результат моих неполных знаний об асинхронном программировании, и я делаю что-то не так.Я упростил фактический код в приведенном ниже примере, но я сохранил ту же последовательность и структуру вызова метода.Я буду благодарен за некоторые подробности о том, что может быть причиной тупика, и за предложения по преодолению этой проблемы.

Я создал класс расширения для HttpClient, который имеет следующие методы:

//HttpClientExtension class
public async static Task<HttpResponseMessage> PostAsyncCustom(this HttpClient client, Uri uri, HttpContent request, bool tryReauth)
{
    var originalResponse = await client.PostAsync(uri, request);
    var content = await originalResponse.Content.ReadAsStringAsync();
    if(tryReauth)
    {
        //check if session expired and login again
        var login = await App.Communication.Login();
        //throw exception with the reauth status
        if(login)
        {
            throw new AuthException(true);
        }
        else
        {
            throw new AuthException(false);
        }
    }
}

public async static Task<HttpResponseMessage> PostAsyncWithReauth(this HttpClient client, Uri uri, HttpContent request)
{
    return await PostAsyncCustom(client, uri, request, true);
}

public async static Task<HttpResponseMessage> PostAsyncWithoutReauth(this HttpClient client, Uri uri, HttpContent request)
{
    return await PostAsyncCustom(client, uri, request, false);
}

Эти методы используются внутри моего класса Communication, который выглядит примерно так:

//Communication class
//HttpClient is defined in contstructor
public async Task<bool> Login()
{
    //Define Uri and Request
    var response = await client.PostAsyncWithoutReauth(uri, request);
    //Check response status and return success/failure
    return true;
}

public async Task<bool> FetchData()
{
    //Define Uri and Request
    try
    {
        var response = await client.PostAsyncWithReauth(uri, request);
    }
    catch(AuthException ae)
    {
        //if reauth was successfull
        if(ae)
        {
            var newResponse = await client.PostAsyncWithoutReauth(uri, request);
            //Check newResponse status and return success/failure
        }
    }
    return false;
}

Итак, что происходит, когда AuthException выдается с status = true и будет вызываться PostAsyncWithoutReauth, чтослучается, что точка останова внутри PostAsyncWithoutReauth будет срабатывать, но при продолжении точка останова внутри PostAsyncCustom, которая вызывается из PostAsyncWithoutReauth, никогда не срабатывает.Код будет продолжать «выполняться», пока не сработает исключение тайм-аута задачи.Вот почему я думаю, что это тупиковая проблема.Я попытался установить .ConfigureAwait(false) для некоторых вызовов, но проблема осталась прежней.

Обновление:

Метод FetchData вызывается изXamarin.Forms ContentPage, где он вызывается завершенным событием в поле «Запись»:

Input.Completed += async (s, e) =>
{
    var status = await App.Communication.FetchData();
}

Я хотел бы добавить, что если вместо вызова PostAsyncWithoutReauth я заменю его на return await FetchData(), это не будеттупик.

Обновление № 2: Я упростил код, поэтому мой PostAsyncCustom теперь выглядит так:

public async static Task<HttpResponseMessage> PostAsyncCustom(this HttpClient client, Uri uri, HttpContent request, bool tryReauth)
{
    var originalResponse = await client.PostAsync(uri, request);
    var content = await originalResponse.Content.ReadAsStringAsync();
    if(tryReauth)
    {
        return await PostAsyncCustom(client, uri, request, false);
    }
    return originalResponse;
}

Что здесь происходит, так это то, что client.PostAsync будет зависать при вызове во второй раз.Я также попытался избавиться от рекурсии, поэтому я протестировал код следующим образом:

public async static Task<HttpResponseMessage> PostAsyncCustom(this HttpClient client, Uri uri, HttpContent request, bool tryReauth)
{
    var originalResponse = await client.PostAsync(uri, request);
    var content = await originalResponse.Content.ReadAsStringAsync();
    if(tryReauth)
    {
        var secondReponse = await client.PostAsync(uri, request);
    }
    return originalResponse;
}

Он также будет зависать при повторном вызове client.PostAsync.

Update #3:

"тупик", наконец, вызывает исключение с помощью трассировки стека, которое можно увидеть здесь .

...