Подтвердить что ты не робот

Windows.Web.Http.HttpClient # GetAsync генерирует неполное исключение, когда недопустимые учетные данные используются с базовой аутентификацией

Я работаю над компонентом Runtime Windows, который вызывает вызовы API. До сегодняшнего дня я использовал HttpClient и связанные с ним модели от System.Net, но вместо этого переключился на Windows.Web, чтобы использовать потоки WinRT.

Помимо изменения операторов using, заменяя HttpContent на IHttpContent и используя WindowsRuntimeExtensions, чтобы изменить мой IInputStream на Stream для JSON.NET, мне не нужно было ничего делать, Однако неожиданно 3 из моих 16 тестов терпят неудачу, тогда как раньше все работало.

Все тесты (интеграция) подтверждают, что я получаю сообщение об ошибке при входе в систему с недопустимыми учетными данными. Существуют и другие тесты, включающие вход в систему (но с действительными учетными данными), и они работают нормально. Данное сообщение об ошибке имеет тип AggregateException и имеет в качестве сообщения

System.AggregateException: Произошла одна или несколько ошибок. --- > System.Exception: Элемент не найден.

Диалоговое окно невозможно отобразить, поскольку дескриптор родительского окна не установлен.

Исключение содержит значения HRESULT. Outerexception имеет значение -2146233088, которое соответствует 0x80131500, в то время как innerexception имеет -2147023728, что соответствует 0x80070490. Ни один из них не является известным кодом ошибки на странице MSDN.

Следующее исследование:

StackTrace:

Result StackTrace:  
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at xx.Models.Requests.GetRequest.<ExecuteRequestAsync>d__0.MoveNext() in c:\Users\jeroen\Github\Windows-app\xx\xx\Models\Requests\Request.cs:line 17

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at xx.ApiDispatcher.<ExecuteAsync>d__0`2.MoveNext() in c:\Users\jeroen\Github\Windows-app\xx\xx\ApiDispatcher.cs:line 40

 --- End of inner exception stack trace ---

    at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at xx.ApiDispatcher.Execute[TCallResult,TResponseObject](ApiCall`2 call) in c:\Users\jeroen\Github\Windows-app\xx\xx\ApiDispatcher.cs:line 22

Первоначально мой вопрос был сформулирован несколько иначе, потому что фактическая проблема казалась скрытой. Я узнал, что запрос GET с помощью HttpClient возвращается обратно вызывающему, а не ожидает результата вызова (и выполняет оставшуюся часть метода).

В моем проекте выполнение строки var data = await myHttpClient.GetAsync(url); вернется к вызывающему методу с непостроенным объектом, а последующие строки, которые появляются после вызова GetAsync(), просто не выполняются.

Добавление .ConfigureAwait(false), чтобы остановить его от возврата, не помогло.

AggregateException вызывается, когда пользователь пытается войти в систему с недопустимыми учетными данными. По какой-то причине HttpClient решает выбросить исключение, не указывая мне возвращаемое значение, которое я мог бы использовать. Проблема здесь в том, что она не говорит мне, какое исключение: catching COMException, TaskCanceledException, AggregateException и Exception вызывает только последний.

Я также выяснил, что тесты асинхронной интеграции не очень хорошо работают с многопоточной средой MSTest, поэтому объясняются несколько других неудачных тестов, которые у меня были (но работали просто отлично)

Я также, наконец, приведу пример, демонстрирующий проблему (но я не могу предоставить веб-сервис, который принимает базовый auth)!

[TestMethod]
public void TestMethod3()
{
    Assert.IsTrue(new Test().Do().AsTask().Result);
}

public sealed class Test
{
   public IAsyncOperation<bool> Do()
   {
       return DoSomething().AsAsyncOperation();
   } 

   private async Task<bool> DoSomething()
   {
       var client = new HttpClient();
       var info = "[email protected]:nopass";
       var token = Convert.ToBase64String(Encoding.UTF8.GetBytes(info));
       client.DefaultRequestHeaders.Authorization = new HttpCredentialsHeaderValue("Basic", token);

       var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
       return true;
   }
}

Выполнение этого кода с допустимым паролем вернет true, в то время как недопустимый пароль вызовет AggregateException.

Сейчас я работаю над проблемой, перехватив общий Exception вокруг вызова GetAsync(), но это очень рудиментарно, и я хотел бы знать, почему это незавершенное исключение выбрасывается в первую очередь.

4b9b3361

Ответ 1

После того, как вы восстановили свой пример и поиграли, я понял, что происходит.

var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));

Метод GetAsync вызывает HTTP-запрос с недопустимыми учетными данными. Случается, что возвращаемый запрос пытается найти окно, в котором вы можете ввести правильные учетные данные, но не найдете его. Следовательно, при поиске этого окна он бросает Element Not Found.

Это можно устранить, создав HttpBaseProtocolFilter и установив для свойства AllowUI значение false, а затем передав его в HttpClient:

private async Task<bool> DoSomething()
{
    var httpBaseFilter = new HttpBaseProtocolFilter
    {
        AllowUI = false
    };

    var client = new HttpClient(httpBaseFilter);
    var info = "[email protected]:nopass";
    var token = Convert.ToBase64String(Encoding.UTF8.GetBytes(info));
    client.DefaultRequestHeaders.Authorization = new HttpCredentialsHeaderValue("Basic", token);

    var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
    return true;
}

Response after adding HttpBaseProtocolFilter

Ответ 2

Настройка AllowUI на HttpBaseProtocolFilter на false остановит эту ошибку.

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

Ответ 3

Я думаю, проблема заключается в том, что исключение фактически вызвано вызовом EnsureSuccessStatusCode.

Вы попробовали добавить метод HttpResponseMessage EnsureSuccessStatusCode() и проверить.

http://msdn.microsoft.com/en-us/library/system.net.http.httpresponsemessage.ensuresuccessstatuscode.aspx

После этой строки:

var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
date.EnsureSuccessStatusCode();