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()
, но это очень рудиментарно, и я хотел бы знать, почему это незавершенное исключение выбрасывается в первую очередь.
Ответы
Ответ 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();