Web API 2 - Global Exception Handler not working with TaskCancellation - c#

Code to demonstrate the problem:
Assume Test Thing is an real implementation, such as DB invocation.
The documentation for Web API states that unhanded exceptions will be caught in a global handler allowing you to process them.
If I replace MyErrorHandler with an ExceptionFilter this does indeed work, except the code base I'm working with uses handlers because the error logic is a cross cutting concern and will be the same regardless of where the error came from.
If the type of exception thrown is not a TaskCancelledException this invokes the handler as expected.
I've tried the latest version of Web API too (5.2.3).
The only work around is to add a try/catch block around everywhere that can throw this type of exception, needless to say this is painful and something I wish to avoid hence the use of the handler.
I hate to call this a bug given this is not my code, but after hours of attempts it's starting to feel that way.
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.ExceptionHandling;
namespace WebApplication3.Controllers
{
public class TestController : ApiController
{
public async Task<string> Get()
{
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
return await new TestThing().ExecuteAsync(cancellationTokenSource.Token);
}
}
public class MyErrorHandler : ExceptionHandler
{
public override Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
public class TestThing
{
public async Task<string> ExecuteAsync(CancellationToken cancellationToken)
{
// Remove this to see the problem, I don't want to add these
// try/catch statements all over the codebase.
try
{
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
}
catch (Exception ex)
{
throw new Exception("Failure...");
}
return await Task.FromResult("Testing...");
}
}
}

Given the lack of suggestions or answers I went with a custom message handler.
public class AsyncFixHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
return await base.SendAsync(request, cancellationToken);
}
catch (TaskCanceledException)
{
// TODO: Log the issue here
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
}
This isn't ideal, but the try/catch is in one place. I'm using this successfully as the solution until something better comes along.

It seems like it's and old bug, and there are some people reporting issues still. I suggest you to create an issue in ASP.NET Github repo and use your workaround or another one for the moment.

Related

Integrating async await into synchronous methods

I've been banging my head against a wall for two days now, and frankly I'm annoyed with myself because I just can't seem to get it.
I'm in a webapi context. During this request I need to send some data to one of our other systems, this system is slow to return, due to heavy calculations and multiple database saves etc etc. I need to log the result of this operation, regardless of whether it is successful or not. But I don't want to wait around for it to finish.
I've read that I should be async await all the way from top to bottom. I would have to convert numerous methods if I decided to do this, as I'm already 3 or 4 methods deep, which I fear would branch out even more.
What are my options here? If I go async await all the way down, what do I do with the methods higher up the stack, like my WebApi controllers?
Here is my code, I've tried to thin it down as much as I can. Right now I'm using Task.Result() in the method PushResult(). Which to my understanding is blocking the async? This code works in that the request gets sent. But the TestLog is always last, not first. Therefore not async.
//I'm in a public service and referenced twice
private void MyEndProcess()
{
// other stuff
_vendorPushService.PushResult(); // This could take a while and I have to wait for it!
_logService.PostLog(LogType.TestLog, "Test");
}
//I'm referenced above and somewhere else in the code base
public void PushResult()
{
ExternalResultModel externalResultModel = _resultService.GetExternalResultModel();
PushedResultModel pushedResult = new PushedResultModel();
try
{
pushedResult = _vendorRequestService.PushResultAsync(externalResultModel).Result;
}
catch (Exception ex)
{
pushedResult.Success = false;
}
if (pushedResult.Success)
{
_logService.PostLog(LogType.SuccessLog, pushedResult.Message);
}
else
{
_logService.PostLog(LogType.FailedLog, pushedResult.Message);
}
}
public async Task<PushedResultModel> PushResultAsync(ExternalResultModel externalResultModel)
{
// setup the requestMessage
HttpResponseMessage responseMessage = await _httpRequestService
.SendRequest(requestMessage)
.ConfigureAwait(false);
return new PushedResultModel
{
Success = responseMessage.IsSuccessStatusCode,
Message = await responseMessage.Content.ReadAsStringAsync()
};
}
public class HttpRequestService : IHttpRequestService
{
private readonly HttpClient _httpClient;
public HttpRequestService(IHttpClientAccessor httpClientAccessor)
{
_httpClient = httpClientAccessor.HttpClient;
}
public async Task<HttpResponseMessage> SendRequest(HttpRequestMessage requestMessage)
{
HttpResponseMessage httpResponseMessage = await _httpClient.SendAsync(requestMessage).ConfigureAwait(false);
return httpResponseMessage;
}
}
You should implement async await all the way from top to bottom.
If I go async await all the way down, what do I do with the methods higher up the stack, like my WebApi controllers?
Just make your controller actions async like this:
[RoutePrefix("api")]
public class PresidentsController : ApiController
{
[Route("presidents")]
public async Task<IHttpActionResult> GetPresidents()
{
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
return Ok();
}
}
It's easiest way to implement async methods. Even if it will add some work to change everything to async it will benefit in future, because You will avoid many problem with async code.
If you absolutly HAVE to use async method in synchronous methods make it block in ONE place, like this:
public void MySyncMethod()
{
try
{
this.MyAsyncMethod().Wait();
}
catch (Exception exception)
{
//omited
}
}
private async Task MyAsyncMethod()
{
await AsyncLogic().ConfigureAwait(false);
}
But i don't recommend it. You should just use async await all the way to controller action.
In your comment you said you want to process a task in the background and not make the client calling your API wait. To do that, you don't really need to use async/await.
Try this:
private void MyEndProcess()
{
// other stuff
Task.Run(_vendorPushService.PushResult()).ConfigureAwait(false); //fire and forget
_logService.PostLog(LogType.TestLog, "Test");
}
The Task.Run will start the task, and the ConfigureAwait(false) tells it that it does not need to resume on the same context that we're currently on (meaning that the context can close before the task is finished - i.e. the response can be sent back without waiting for the task to finish).
You will get a compiler warning that you're not awaiting Task.Run, but that's what you want.
Keep in mind that when you do this, HttpContext.Current will not be available inside PushResult.

Using Task<T>.IsFaulted to denote IO failures without propagating exceptions

Before/without Task<>s, I'm using a wrapper object like the below to encapsulate the result of an I/O operation that could fail, without propagating exceptions up the call stack:
public class FetchResult<T>
{
public readonly bool Success;
public readonly T Item;
public FetchResult(bool success, T item)
{
this.Success = success;
this.Item = item;
}
}
I'd use it like this:
var userResult = Get("robert.paulson#fightclub.com");
if(!userResult.Success)
// abort, or something
...
public FetchResult<User> Get(string email)
{
try
{
// go to database here, and get the User
return new FetchResult(true, new User());
}
catch
{
// log exception
return new FetchResult(false, null);
}
}
This works great for me as a model, as it allows me to effectively manage exceptions without using try/catch as program control flow and gives me easy and fine-grained graceful service degredation.
However, with the advent of Task<>, I could very easily end up with:
public Task<FetchResult<User>> GetAsync(string email)
which seems to be getting out of hand.
Seeing as I'm migrating to async everywhere anyway, I'm contemplating just doing:
public Task<User> GetAsync(string email)
which id have expected to allow me to do something like:
var userTask = GetAsync("robert.paulson#fightclub.com");
await userTask;
if(userTask.IsFaulted) // (*) - see below
// abort, or something
However if my GetAsync method returns:
return Task<User>.FromException(new Exception());
what is actually returned after the await (where the (*) comment is) seems to be a Completed Task, whos result is a Task<User> which is faulted.
Why am I getting a nested task in this case, and is there some syntactic sugar I'm missing to make this whole affair tidier?
There are two methods in Task library FromException() and FromException<TResult>() both are available via Task and Task<TResult>.
public static Task FromException(Exception exception)
{
return FromException<VoidTaskResult>(exception);
}
public static Task<TResult> FromException<TResult>(Exception exception)
{
...
...
}
If you call Task<TResult>.FromException() or Task.FromException() there's no different in these two calls.
Your method signature is: public Task<User> GetAsync(string email)
Now if you try to use Task<User>.FromException(new Exception()) this will return Task<VoidTaskResult> and which is ofcourse is not of type Task<User>. Which means you might be getting compiler error.
This compile time error would be gone if you use Task<User>.FromException<User>(new Exception()); or Task.FromException<User>(new Exception());
You're getting Task<Task<VoidTaskResult>> as specified in comments means there's something more in your method code which is not mentioned in your sample code.
For more internal details about Task methods in .Net source code see here
Update:
After looking at your code found couple of issues.
i) Return type as Task is avoiding the compilation error that I mentioned earlier in my answer.
ii) You're returning a Task with in a Async method without awaiting means the complete task object will be wrapped in Another Task type.
See below example that I tweaked to to show the problem. See even the return type is changed from Task of type Object to Program there's still no error. This is Because of Object which is base of any custom type in C#. So doing below is allowed:
static async Task<object> GetAsync()
{
try
{
throw new Exception();
}
catch (Exception e)
{
return Task.FromException<Program>(e);
}
}
Now change the return type of method to Task<Program> you'll get an error or more of warning.
Now the correct version to fix the problem would be to await the Task so that only Program type remain as result which will be automatically returned as Task<Program>.
Correct version:
static async Task<Program> GetAsync()
{
try
{
throw new Exception();
}
catch (Exception e)
{
return await Task.FromException<Program>(e);
}
}
Now you won't see any Nested tasks anymore. This is internals of how await works. If you really want know why it happens like this then try to analyze the IL generated of all the above 3 versions of the program using IL spy and you'll get the idea. Cheers!!!
Kind of unusual, but you can explore the fact that the Task.WhenAny method does not throw exception (see Is there a way to Wait for a TPL Task without in throwing an exception?) like this
var userTask = GetAsync("robert.paulson#fightclub.com");
await Task.WaitAny(userTask);
if(userTask.IsFaulted) // (*) - see below
// abort, or something
Task.IsFaulted means that the Task completed due to an unhandled exception. So I don't think you could set it in a different way than to let the exception go uncaught.
public async Task<User> GetAsync(string email)
{
// go to database here, and get the User
return new User();
}
Or catch, log, and rethrow:
public async Task<User> GetAsync(string email)
{
try
{
// go to database here, and get the User
return new User();
}
catch
{
// log exception
throw;
}
}
I believe you are saying that your GetAsync is returning a nested task because you have something like this:
public async Task GetAsync(string email)
{
// .... somewhere in the code
return Task.FromException(new Exception());
}
But when you declare a method using the async keyword, C# magic takes care of wrapping its contents inside a Task. So in this case you actually end up having a task which returns another task - Task<Task<T>>. It would return Task<T> if you removed the async keyword but then obviously you end up with a standard synchronous method.

Global Exception handler that returns same as InternalServerError(ex)

I have a few controllers. Each one has a few methods. Each method has a try/catch block that contains the same thing:
catch (Exception ex)
{
return InternalServerError(ex);
}
This returns the exception information as json, which is what I need. I'm pretty sure it's just the exception object serialized as json.
What I would like to do is have a global exception handler so I can get rid of all the try / catch blocks and clean up my controllers.
Here's the catch. The samples I've seen don't return what I need to return. That's why I think this is not a duplicate.
Here's my code for those who want to see it.
public class GlobalExceptionHandler : IExceptionHandler
{
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
throw new NotImplementedException();
//HttpResponseMessage response =
// new HttpResponseMessage(HttpStatusCode.InternalServerError);
//response.Content = new StringContent(JsonConvert.SerializeObject(con);
//response.RequestMessage = Request;
//return Task.FromResult(response);
}
}
Once I found the right article, it turned out to be easy:
public class GlobalExceptionHandler : IExceptionHandler
{
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
return Task.FromResult(context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, context.Exception.Message));
}
}

catch all unhandled exceptions in ASP.NET Web Api

How do I catch all unhandled exceptions that occur in ASP.NET Web Api so that I can log them?
So far I have tried:
Create and register an ExceptionHandlingAttribute
Implement an Application_Error method in Global.asax.cs
Subscribe to AppDomain.CurrentDomain.UnhandledException
Subscribe to TaskScheduler.UnobservedTaskException
The ExceptionHandlingAttribute successfully handles exceptions that are thrown within controller action methods and action filters, but other exceptions are not handled, for example:
Exceptions thrown when an IQueryable returned by an action method fails to execute
Exceptions thrown by a message handler (i.e. HttpConfiguration.MessageHandlers)
Exceptions thrown when creating a controller instance
Basically, if an exception is going to cause a 500 Internal Server Error to be returned to the client, I want it logged. Implementing Application_Error did this job well in Web Forms and MVC - what can I use in Web Api?
This is now possible with WebAPI 2.1 (see the What's New):
Create one or more implementations of IExceptionLogger. For example:
public class TraceExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
Trace.TraceError(context.ExceptionContext.Exception.ToString());
}
}
Then register with your application's HttpConfiguration, inside a config callback like so:
config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
or directly:
GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
To answer my own question, this isn't possible!
Handling all exceptions that cause internal server errors seems like a basic capability Web API should have, so I have put in a request with Microsoft for a Global error handler for Web API:
https://aspnetwebstack.codeplex.com/workitem/1001
If you agree, go to that link and vote for it!
In the meantime, the excellent article ASP.NET Web API Exception Handling shows a few different ways to catch a few different categories of error. It's more complicated than it should be, and it doesn't catch all interal server errors, but it's the best approach available today.
Update: Global error handling is now implemented and available in the nightly builds! It will be released in ASP.NET MVC v5.1. Here's how it will work: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling
The Yuval's answer is for customizing responses to unhandled exceptions caught by Web API, not for logging, as noted on the linked page. Refer to the When to Use section on the page for details. The logger is always called but the handler is called only when a response can be sent. In short, use the logger to log and the handler to customize the response.
By the way, I am using assembly v5.2.3 and the ExceptionHandler class does not have the HandleCore method. The equivalent, I think, is Handle. However, simply subclassing ExceptionHandler (as in Yuval's answer) does not work. In my case, I have to implement IExceptionHandler as follows.
internal class OopsExceptionHandler : IExceptionHandler
{
private readonly IExceptionHandler _innerHandler;
public OopsExceptionHandler (IExceptionHandler innerHandler)
{
if (innerHandler == null)
throw new ArgumentNullException(nameof(innerHandler));
_innerHandler = innerHandler;
}
public IExceptionHandler InnerHandler
{
get { return _innerHandler; }
}
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
Handle(context);
return Task.FromResult<object>(null);
}
public void Handle(ExceptionHandlerContext context)
{
// Create your own custom result here...
// In dev, you might want to null out the result
// to display the YSOD.
// context.Result = null;
context.Result = new InternalServerErrorResult(context.Request);
}
}
Note that, unlike the logger, you register your handler by replacing the default handler, not adding.
config.Services.Replace(typeof(IExceptionHandler),
new OopsExceptionHandler(config.Services.GetExceptionHandler()));
You can also create a global exception handler by implementing the IExceptionHandler interface (or inherit the ExceptionHandler base class). It will be the last to be called in the execution chain, after all registered IExceptionLogger:
The IExceptionHandler handles all unhandled exceptions from all
controllers. This is the last in the list. If an exception occurs, the
IExceptionLogger will be called first, then the controller
ExceptionFilters and if still unhandled, the IExceptionHandler
implementation.
public class OopsExceptionHandler : ExceptionHandler
{
public override void HandleCore(ExceptionHandlerContext context)
{
context.Result = new TextPlainErrorResult
{
Request = context.ExceptionContext.Request,
Content = "Oops! Sorry! Something went wrong."
};
}
private class TextPlainErrorResult : IHttpActionResult
{
public HttpRequestMessage Request { get; set; }
public string Content { get; set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response =
new HttpResponseMessage(HttpStatusCode.InternalServerError);
response.Content = new StringContent(Content);
response.RequestMessage = Request;
return Task.FromResult(response);
}
}
}
More on that here.
You may have existing try-catch blocks that you're not aware of.
I thought my new global.asax.Application_Error method wasn't being consistently called for unhandled exceptions in our legacy code.
Then I found a few try-catch blocks in the middle of the call stack that called Response.Write on the Exception text. That was it. Dumped the text on the screen then killed the exception stone dead.
So the exceptions were being handled, but the handling was doing nothing useful. Once I removed those try-catch blocks the exceptions propagated to the Application_Error method as expected.

Customizing SignalR's error messages

I use the SignalR 1.0.
When exception occurs on the server, the client gets a message like this
{"I":"0","E":"Exception of type 'System.Exception' was thrown.","T":" at METHODNAME in d:\PATH\TO\HUB.cs:line 227\r\n at METHODNAME in d:\PATH\TO\HUB.cs:line 51"}
But I want to make it more user-friendly. How to I can do it?
I have read a suggestion to put all server methods into try-catch block. But I think that it is not a true-way.
I traced the Exception and found that the Exception was catched in the Microsoft.AspNet.SignalR.Hubs.HubDispatcher.Incoming method. But it is internal static method which I cannot customize.
In the ideal case I want to get ability to convert an exception to a valid response.
You can use a HubPipelineModule.
For example:
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Hubs;
public class MyHubPipelineModule : HubPipelineModule
{
protected override Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke)
{
return async context =>
{
try
{
// This is responsible for invoking every server-side Hub method in your SignalR app.
return await invoke(context);
}
catch (Exception e)
{
// If a Hub method throws, have it return the error message instead.
return e.Message;
}
};
}
}
Then in your Global.asax.cs:
protected void Application_Start()
{
GlobalHost.HubPipeline.AddModule(new MyHubPipelineModule());
//...
RouteTable.Routes.MapHubs();
}

Categories