C# HttpClient 通过 VPN 抛出 System.OutOfMemoryException

C# HttpClient throws System.OutOfMemoryException over VPN

我有一个通过 VPN 使用 Web API 的 WPF 桌面应用程序。相关详情:

有时它会在应用程序启动时从 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 class 实现的相关部分

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 不是调用异步任务的正确方法,但我必须在启动时做一些,但我的问题是 [=21= 抛出的奇怪 OutOfMemoryException ].如果它确实重要,return API 调用的值是一个 4 字节整数,所以我不会尝试下载 8K 蓝光并且客户端上有足够的内存。

主要观察结果:

最后,我找到并解决了问题。实际问题在这里:

Credentials = CredentialCache.DefaultNetworkCredentials

HttpClient 似乎无法读取凭据缓存内容(对此一无所知)。修复方法是将该行替换为:

UseDefaultCredentials = true

一切都开始正常工作了。