Асинхронный метод не возвращается в фильтре авторизации MVC - PullRequest
0 голосов
/ 31 мая 2018

У меня есть одно тестовое приложение, в котором работает код, подобный следующему, но похожий код не работает в другом приложении:

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
    var user = GetUser().GetAwaiter().GetResult();
}

private async Task<User> GetUser()
{
    var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
    client.BaseAddress = new Uri(baseUrl);
    client.Timeout = Timeout.InfiniteTimeSpan;
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

    var request = new UserSearchRequest
        {
            DomainName = "my-corp-domain\my-user-id"
        };

    var response = await client.PostAsJsonAsync("api/v1/users/search", request);
    var users = (await response.Content.ReadAsAsync<List<User>>());
    return users.FirstOrDefault();
}   

В проблемном приложении вызывается PostAsJsonAsync, но он никогда не возвращается.Любая идея, что проблема может быть или как отладить?

1 Ответ

0 голосов
/ 01 июня 2018

Отказ от ответственности: Выполнение асинхронного метода внутри синхронного метода часто приводит к тупикам и проблемам с производительностью.Предпочтительным является полностью асинхронный путь вызова или даже полностью синхронный путь.Если вам абсолютно необходимо заблокировать асинхронные методы, читайте дальше.

У меня были подобные случаи, когда запросы HttpClient никогда не возвращались, и ответ обычно был тупиковым.@Eser предоставил ссылку на другой вопрос, который хорошо описывает тупиковый процесс.Подводя итог, вызов GetResult() заблокировал поток.В то же время PostAsJsonAsync ожидает разблокировки того же потока, чтобы он мог завершить обработку, создавая тем самым тупик.

Пример асинхронного ожидания / ожидания, который вызывает взаимоблокировку

Одной из возможностей является использование ConfigureAwait(false).Это говорит PostAsJsonAsync, что он может завершить обработку в другом контексте, а не ожидать разблокировки исходного контекста.Тогда функция GetUser() будет выглядеть следующим образом:

private async Task<User> GetUser()
{
    var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
    client.BaseAddress = new Uri(baseUrl);
    client.Timeout = Timeout.InfiniteTimeSpan;
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));

    var request = new UserSearchRequest
    {
        DomainName = "my-corp-domain\my-user-id"
    };

    var response = await client.PostAsJsonAsync("api/v1/users/search", request).ConfigureAwait(false);
    var users = await response.Content.ReadAsAsync<List<User>>().ConfigureAwait(false);
    return users.FirstOrDefault();
}   

Другое решение, которое я использовал в прошлом, - это обернуть синхронное выполнение внутри Task.Run.Это помещает начало асинхронного метода в другой контекст, поэтому оба метода не будут пытаться заблокировать один и тот же.Затем метод AuthorizeCore будет выглядеть следующим образом:

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
    var user = Task.Run(() => GetUser()).GetAwaiter().GetResult();
}

Для получения дополнительной информации об этом методе см. Должен ли я предоставлять синхронные оболочки для асинхронных методов?

Еще раз подчеркиваю, что ни одно из них не является идеальным решением.Выполняя операции «синхронизация по асинхронному», вы теряете все преимущества асинхронной функциональности, одновременно повышая вероятность взаимоблокировок и потенциально худшую производительность.Я рекомендую использовать асинхронный режим до тех пор, пока это возможно.Когда это не так, придерживаться «старых» синхронных методов - не самый плохой вариант.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...