I have a simple API controller method
public async Task<Models.Timesheet> GetByDate(DateTime date, string user = null)
{
throw new InvalidOperationException();
}
Now the problem is that the exception stack trace I get either in my custom action filter or just by setting IncludeErrorDetailPolicy.Always
is like this
System.InvalidOperationException: Operation is not valid due to the current state of the object.
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__1.MoveNext()
--- 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 System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- 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.Web.Http.ApiController.<InvokeActionWithExceptionFilters>d__1.MoveNext()
It used to be much better with Web API v1. After upgrade to v2 the stack traces are pretty much unusable - it is expected that with async/await the stack traces will not be as they once used to be but in this case the whole stack trace does not include any hint at even the class that failed. Is there something that can be configured in Web API to mitigate the issue?
I believe stack traces are improved on .NET 4.5.1 when running on Windows 8.1 / Server 2012 R2.
Alternatively, I do have an "Async Diagnostics" NuGet package that you can install into your project. Then add this line:
And you can use the
ToAsyncDiagnosticString
extension method on theException
type.ToAsyncDiagnosticString
includes all the information fromToString
and then appends a "logical stack". More documentation (and the source) on GitHub.Note that there is no support for partial trust and this works best in Debug builds.
So after I asked the question I got the right motivation to properly dig through Web API code to find the issue. It seems that the culprit is
ActionFilterAttribute.CallOnActionExecutedAsync
method that does something like this (paraphrased, not the actual code):So it happens that the first filter actually gets the correct stack trace. But then it just goes and rethrows the exception, thus losing the original stack trace.
This made me realize that instead of deriving from
ExceptionFilterAttribute
, I need to derive fromActionFilterAttribute
since the former are always called last. Unfortunately I also need to make sure my exception filter runs first but that is not built-in Web API (see this question for a correct solution for this).My quick solution was to make sure all filters I have in the application (I only have a few) derive from my custom exception filter (and call
base.OnActionExecuted()
) thus every single filter will actually check for the exception and perform something like this:With this workaround I got the correct stack traces both to the log and to the user if error details are turned on.
This has been fixed in WebApi 2.1 (assembly version 5.1.1)
Follow these instructions to upgrade to the latest version: https://www.nuget.org/packages/Microsoft.AspNet.WebApi