【发布时间】:2021-04-22 10:32:53
【问题描述】:
我有一个 WPF 桌面应用程序,它通过 VPN 使用 Web API。相关细节:
- 应用类型:WPF
- .NET:4.6.2
- 操作系统:Windows 10
- VPN:帕洛阿尔托 GlobalProtect
有时它会在应用程序启动时从 HttpClient 抛出未捕获的异常:
Error message: Exception of type 'System.OutOfMemoryException' was thrown.
Stack trace:
at MyApp.Net.Services.RestNetClient.<GetMyRoles>d__5.MoveNext()
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine& stateMachine)
at MyApp.Net.Services.RestNetClient.GetMyRoles()
<...>
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at MyApp.Wpf.UI.App.<OnStartup>d__3.MoveNext() in <...>\App.xaml.cs:line 44
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
这是RestNetClient类实现的相关部分
public class RestNetClient : INetClient {
public RestNetClient(IRemoteConfig config) {
if (Client == null) {
HttpClientHandler authHandler = new HttpClientHandler {
Credentials = CredentialCache.DefaultNetworkCredentials
};
Client = new HttpClient(authHandler) { BaseAddress = config.ServerAddress };
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
protected static HttpClient Client { get; private set; }
public async Task<IServiceOperationResult<AppRole>> GetMyRoles() {
var result = new ServiceOperationResult<AppRole>();
try {
HttpResponseMessage response = await Client.GetAsync("api/ntds/getmyroles");
if (response.IsSuccessStatusCode) {
String json = await response.Content.ReadAsStringAsync();
result.Value = JsonConvert.DeserializeObject<AppRole>(json);
} else {
await response.SetErrorCode(result);
}
} catch (Exception ex) {
result.HResult = ErrorCode.E_INVALIDARG;
result.ErrorMessage = ex.Message;
}
return result;
}
<...>
GetMyRoles 方法的 catch 子句中没有捕获异常,我在 WPF 的 App.Dispatch.UnhandledException 事件处理程序中得到它。我从App.OnStartup方法调用这个方法,像这样:
protected override async void OnStartup(StartupEventArgs e) {
<...>
var result = await restClient.GetMyroles();
<...>
}
我知道async void 不是调用异步任务的正确方法,但我必须在启动时做一些事情,但我的问题是HttpClient 抛出的奇怪OutOfMemoryException。如果它确实重要,API 调用的返回值是一个 4 字节整数,所以我不会尝试下载 8K 蓝光并且客户端上有足够的内存。
主要观察:
- 偶尔会抛出异常
- 仅在应用启动时引发异常(此特定 API 端点调用仅在启动时调用一次)
- 如果第一次调用成功,那么所有后续 API 调用都会按预期工作,并且在正常情况下永远不会失败
- 多个客户端报告的问题,唯一常见的是:它们通过 Palo Alto VPN 客户端连接到服务器
- 当客户端直接连接到服务器(没有 VPN)时,问题永远不会出现
- 客户端上还有其他应用程序通过相同的 VPN 进行通信,它们似乎没有任何通过 VPN 的连接问题。
【问题讨论】:
-
请注意,您是否考虑过用异步启动事件处理程序替换异步 OnStartup 覆盖,这将是使用 async void 的正确方法?
-
@Clemens 在同一个 App.xaml.cs 文件中?我可以,只要有理由这样做。不过,我不知道我的情况会发生什么变化。
-
是的,完全正确。它不会解决您的问题,但会使 async void 声明“正确”。
-
@Clemens 好的,我会考虑的。
-
提示
HttpResponseMessage是IDisposable。添加using。
标签: c# wpf dotnet-httpclient