私は、API呼び出しを行うWindowsランタイムコンポーネントに取り組んでいます。今日まで、私はHttpClient
と_System.Net
_の関連モデルを使用しましたが、WinRTストリームを利用する代わりに_Windows.Web
_に切り替えました。
using
ステートメントを変更すること、HttpContent
をIHttpContent
にスワップすること、およびWindowsRuntimeExtensions
を使用してJSON.NETのIInputStream
をStream
に変更すること以外に、特別なことをする必要はありませんでした。しかし、突然、私の16テストのうち3テストが失敗しました。
3(統合)テストはすべて、無効な資格情報でログインしたときにエラー応答が返されることを検証します。 (有効な資格情報を使用した)ログインも含む他のテストがあり、それらは正常に動作します。指定されたエラーメッセージはAggregateException
タイプであり、メッセージとして
_
System.AggregateException
_:1つ以上のエラーが発生しました。 ---> _System.Exception
_:要素が見つかりません。親ウィンドウハンドルが設定されていないため、ダイアログを表示できません。
例外にはHRESULT値が含まれます。 outerexceptionには_-2146233088
_に対応する_0x80131500
_の値があり、innerexceptionには_-2147023728
_に対応する_0x80070490
_があります。これらはいずれも MSDNページ の既知のエラーコードではありません。
次の調査:
スタックトレース:
_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
_
もともと実際の問題は隠されているようだったので、私の質問は少し違った言い方をされました。 HttpClient
によるGETリクエストが、呼び出しの結果を待つ(およびメソッドの残りの部分を実行する)代わりに、呼び出し元に戻ることがわかりました。
私のプロジェクトでは、var data = await myHttpClient.GetAsync(url);
行を実行すると、構築されていないオブジェクトを含む呼び出し元のメソッドに戻り、GetAsync()
呼び出しの後に続く行は実行されません。
.ConfigureAwait(false)
を追加して元に戻らないようにしても、違いはありませんでした。
AggregateException
は、ユーザーが無効な認証情報でログインしようとするとスローされます。何らかの理由で、HttpClient
は、使用できる戻り値を返さずに例外をスローすることにしました。ここでの問題は、それがどのような例外を通知しないかです:COMException
、TaskCanceledException
、AggregateException
、およびException
をキャッチすると、後者のみがトリガーされます。
非同期統合テストはマルチスレッドのMSTest環境ではうまく機能しないこともわかりました。これにより、他のいくつかの失敗したテストが説明されます(ただし、個別にうまく機能しました)。
最後に、問題を示す例もあります(ただし、基本的な認証を行うWebサービスは提供できません)。
_[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
がスローされます。
現在、私はGetAsync()
の呼び出しに関する一般的なException
をキャッチすることで問題を回避していますが、これは非常に初歩的なものであり、この不完全な例外が最初にスローされる理由を知りたいです。
例を再構築して遊んだ後、私は何が起こるかを理解しました。
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;
}
AllowUI
のHttpBaseProtocolFilter
をfalse
に設定すると、このエラーが停止します。
ただし、ユーザーが資格情報を入力できるようにダイアログボックスを表示したい場合は、UIスレッドでWeb要求を開始する必要があります。
問題は、EnsureSuccessStatusCodeの呼び出しによって実際に例外がスローされることだと思います。
HttpResponseMessageクラスのEnsureSuccessStatusCode()メソッドを追加して確認しましたか?.
この行の後:
var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
date.EnsureSuccessStatusCode();