我在.NET Framework4.8 (WCF)中有一个应用程序,它进行http调用,也使用Polly进行重试和回退管理,但有时会引发System.NullReferenceException,但我不知道问题出在哪里。这是代码:
private static async Task<bool> CallApi<TRequest>(TRequest request, string requestUri, Func<string, StringContent, Task<HttpResponseMessage>> func)
{
var jsonRequest = JsonConvert.SerializeObject(request);
var fallBackPolicy = Policy<HttpResponseMessage>.Handle<Exception>()
.FallbackAsync(new HttpResponseMessage(HttpStatusCode.SeeOther)
{
Content = new StringContent($"Exception has occurred in migration call. RequestUri: {requestUri}")
},
result =>
{
LogEventService.Logger.Error(result.Exception, "An unhandled exception occurred while retrying calling");
return Task.CompletedTask;
});
var waitAndRetryPolicy = Policy.HandleResult<HttpResponseMessage>(res => res.StatusCode == HttpStatusCode.InternalServerError).
WaitAndRetryAsync(2, retryAttempts => TimeSpan.FromMilliseconds(500));
var response = await fallBackPolicy
.WrapAsync(waitAndRetryPolicy)
.ExecuteAsync(async () =>
{
using (var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json"))
{
content.Headers.Add("X-Correlation-ID", HttpContext.Current.Session[RequestId].ToString());
return await func(requestUri, content);
}
});
if(response.IsSuccessStatusCode)
return true;
await LogMessage(LogLevel.Error, response, requestUri);
return false;
}这是StackTrace
System.NullReferenceException:对象引用没有设置为对象的实例。 在UserMigrationService.<>c__DisplayClass22_0'1.d.MoveNext()在...\Services\UserMigrationService.cs:line 474年 -从抛出异常的前一个位置开始的堆栈跟踪的结束--在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()。 在Polly.Retry.AsyncRetryEngine.d__0'1.MoveNext() -从抛出异常的前一个位置开始的堆栈跟踪的结束--在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()。 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务) at‘1.d_13.13.MoveNext() -从抛出异常的前一个位置开始的堆栈跟踪的结束--在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()。 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务) 在Polly.Wrap.AsyncPolicyWrapEngine.<>c__DisplayClass0_0'1.d.MoveNext() -从抛出异常的前一个位置开始的堆栈跟踪结束-- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() > at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务) 在Polly.Fallback.AsyncFallbackEngine.d__0'1.MoveNext()
你能帮我找错地方吗?如何更好地理解抛出此异常的原因?
谢谢大家
发布于 2021-07-16 09:32:35
实际上,NRE被抛到了这一行:
ontent.Headers.Add("X-Correlation-ID", HttpContext.Current.Session[RequestId].ToString());这表明HttpContext.Current可能是null。ExecuteAsync接收的委托可能与CallApi方法的其余代码不在同一线程上运行。
这就是为什么HttpContext不会流到委托中。
修复非常容易:您必须在CallApi中捕获CallApi,而不是在ExecuteAsync委托中捕获:
var correlationId = HttpContext.Current.Session[RequestId].ToString();
var response = await fallBackPolicy
.WrapAsync(waitAndRetryPolicy)
.ExecuteAsync(async () =>
{
using (var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json"))
{
content.Headers.Add("X-Correlation-ID", correlationId);
return await func(requestUri, content);
}
});我还建议使用Policy.Wrap (参考文献),而不是在其中一个策略上调用WrapAsync方法。以下两行相等:
fallBackPolicy.WrapAsync(waitAndRetryPolicy)
Policy.WrapAsync(fallbackPolicy, waitAndRetryPolicy)因此,您的代码可以这样重写:
var correlationId = HttpContext.Current.Session[RequestId].ToString();
var strategy = Policy.WrapAsync(fallbackPolicy, waitAndRetryPolicy);
var response = await strategy
.ExecuteAsync(async (ct) =>
{
using (var content = new StringContent(jsonRequest, Encoding.UTF8, MediaTypeNames.Application.Json))
{
content.Headers.Add("X-Correlation-ID", correlationId);
//TODO: pass the cancellationToken to the func
return await func(requestUri, content);
}
}, CancellationToken.None);我使用了ExecuteAsync的另一个重载,在这里您正在接收一个CancellationToken。无论何时考虑使用TimeoutPolicy,这都是非常有用的。在这种情况下,您应该将该令牌传递给func函数。
https://stackoverflow.com/questions/68392013
复制相似问题