Which one takes priority, ExceptionFilter or ExceptionHandler in ASP.NET Web Api 2.0? - asp.net-web-api

I have a global ExceptionHandler in my web api 2.0, which handles all unhandled exceptions in order to return a friendly error message to the api caller.
I also have a global ExceptionFilter, which handles a very specific exception in my web api and returns a specific response. The ExceptionFilter is added dynamically by a plugin to my web api so I cannot do what it does in my ExceptionHandler.
I am wondering if I have both the ExceptionHandler and the ExceptionFilter registered globally, which one will take priority and be executed first? Right now I can see that the ExceptionFilter is being executed before the ExceptionHandler. And I can also see that in my ExceptionFilter if I create a response the ExceptionHandler is not being executed.
Will it be safe to assume that:
ExceptionFilters are executed before ExceptionHandlers.
If the ExceptionFilter creates a response, the ExceptionHandler will not be executed.

I had to debug through the System.Web.Http in order to find the answer to my question. So the answer is:
It is safe to assume that ExceptionFilters will be executed before ExceptionHandlers
If the ExceptionFilter creates a response the ExceptionHandler would not be executed.
Why this is so:
When you have an ExceptionFilter registered to execute globally or for your controller action, the ApiController base class from which all the api Controllers inherit will wrap the result in an ExceptionFilterResult and call its ExecuteAsync method. This is the code in the ApiController, which does this:
if (exceptionFilters.Length > 0)
{
IExceptionLogger exceptionLogger = ExceptionServices.GetLogger(controllerServices);
IExceptionHandler exceptionHandler = ExceptionServices.GetHandler(controllerServices);
result = new ExceptionFilterResult(ActionContext, exceptionFilters, exceptionLogger, exceptionHandler,
result);
}
return result.ExecuteAsync(cancellationToken);
Looking at the ExceptionFilterResult.ExecuteAsync method:
try
{
return await _innerResult.ExecuteAsync(cancellationToken);
}
catch (Exception e)
{
exceptionInfo = ExceptionDispatchInfo.Capture(e);
}
// This code path only runs if the task is faulted with an exception
Exception exception = exceptionInfo.SourceException;
Debug.Assert(exception != null);
bool isCancellationException = exception is OperationCanceledException;
ExceptionContext exceptionContext = new ExceptionContext(
exception,
ExceptionCatchBlocks.IExceptionFilter,
_context);
if (!isCancellationException)
{
// We don't log cancellation exceptions because it doesn't represent an error.
await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
}
HttpActionExecutedContext executedContext = new HttpActionExecutedContext(_context, exception);
// Note: exception filters need to be scheduled in the reverse order so that
// the more specific filter (e.g. Action) executes before the less specific ones (e.g. Global)
for (int i = _filters.Length - 1; i >= 0; i--)
{
IExceptionFilter exceptionFilter = _filters[i];
await exceptionFilter.ExecuteExceptionFilterAsync(executedContext, cancellationToken);
}
if (executedContext.Response == null && !isCancellationException)
{
// We don't log cancellation exceptions because it doesn't represent an error.
executedContext.Response = await _exceptionHandler.HandleAsync(exceptionContext, cancellationToken);
}
You can see that the ExceptionLogger is executed first, then all ExceptionFilters are executed and then if if executedContext.Response == null, the ExceptionHandler is executed.
I hope this is useful!

Related

Handle Exception From Within Method

I am implementing payments for my website using the API of an external service (ie. the service of the payment provider).
Let's say the user clicks 'BUY', and then we go to my controller which says something along the lines of:
public function buyFunction() {
$result = $this->ExternalService->pay();
if ($result->success == true) {
return 'We are happy';
}
}
I have also created the aforementioned externalService which has the pay() method:
class ExternalService {
public function pay() {
response = //Do stuff with Guzzle to call the API to make the payment
return response;
}
}
Now, sometimes things go wrong.
Let's say the API returns an error - which means that it throws a GuzzleException - how do I handle that?
Ideally, if there is an error, I would like to log it and redirect the user to a page and tell him that something went wrong.
What I've tried
I have tried using a try/catch statement within the pay() function and using abort(500) but this doesn't allow me to redirect to the page I want to.
I have tried using a try/catch statement within the pay() function and using return redirect('/mypage') but this just returns a Redirect object to the controller, which then fails when it tries to call result->success
I have tried using number 2 but also adding a try/catch block to the controller method, but nothing changed.
In the end, I have found two solutions. In both, I use a try/catch block inside the pay() method. Then I either return 0; and check in the controller if (result == 0) or I use abort( redirect('/mypage') ); inside the try/catch block of the pay() method.
What is the right way to handle this?
How to use the try/catch blocks?
In my experience, avoid handling exceptions let them pass through and handle them accordingly with try catches. This is the most pragmatic approach. Alternatively you will end up checking result is correct in weird places, eg. if ($result) {...}. Just assume it went good, except if the exception is thrown. Bonus: never do Pokemon catches with Exception $e unless you specifically needs it!
class ExternalService {
public function pay() {
try {
response = $client->get(...);
} catch (BadResponseException $exception) {
Log::warning('This should not happen check payment api: ' . $exception->getMessage());
throw new PaymentException('Payment did not go through');
}
return response;
}
}
Assuming you have your own Exception.
class PaymentException extends HttpException
{
public function __construct(?\Exception $previous = null)
{
parent::__construct(Response::HTTP_BAD_REQUEST, 'Unexpected error processing the payment', $previous);
}
}
This enables you to handle the flow in a controller, where it would make sense to handle the redirect. Sometimes if the exception is very integral or common to the web app, it can also be handled by the exception handler instead.
class PaymentController {
public function pay(PaymentService $service) {
try {
$payment = $service->buyFunction();
} catch (PaymentException $exception) {
return redirect()->route('app.payment.error');
}
return view('app.payment.success', compact('payment'));
}
}

Spring boot async call with CompletableFuture, exception handling

I have a Spring boot service with some code like below for parallel async call:
CompletableFuture future1 = accountManager.getResult(url1);
CompletableFuture future2 = accountManager.getResult(url2);
CompletableFuture.allOf(future1, future2).join();
String result1 = future1.get();
String result2 = future2.get();
It works fine when there is no exception. My question is how to handle exception? If getting future1 failed (let say url2 is an invalid url), I still want future2 back as partial result of allOf method. How should I do it?
Thanks!
CompletableFuture comes with a block called exceptionally() which can be used handle the exceptions happen inside the asynchronous code block. Snippet of getResult method for your reference,
public CompletableFuture<String> getGreeting(String url) {
return CompletableFuture.supplyAsync( () -> {
return // Business logic..
}, executor).exceptionally( ex -> {
log.error("Something went wrong : ", ex);
return null;
});
}
In this case the block would return null in case of exception and allOf method would lead to a completion where you can filter the one resulted in the exception when you fetch individual futures.

Spring aspect for crosscutting process pipeline usage

I want to use spring aspect for my crosscutting process like a before or after handler in my process execution lifetime.
For example
What I want before a method I want to execute my handler and due to its response I want to finialize myprocess and return custom response.
In example in stop condition should I throw custom exception to stop process that time how can I handle my return response ,I want to give meaningfull object at the client.What is the best way to do that?
#Before
MyHandler()
{
bool stop=checkvalue();
if(stop==false)
continue...
else
{
//break all process
and return custom response to client
//throw exception but meaningfullresponse??
}
}
Instead of using Before advice, I would use Around advice to wrap the method invocation and call checkValue() on beforehand:
#Around("someJoinpoint")
public Object MyHandler(ProceedingJoinPoint pjp) {
if (!checkvalue()) {
return pjp.proceed();
} else {
return someCustomResponseToClient();
}
}
More info on Around advice can be found in the Spring documentation.

Exception Handler for normal AND ajax request for logging purpose

Can someone give me a working example of an ExceptionHandler which extends ExceptionHandlerWrapper for both normal and ajax request. I don't want to use a filter. I want all my exception handling to be in one entry place for logging purpose.
In Omnifaces we have the FullAjaxExceptionHandler but only for ajax request. How can I refactor this class in order to take into account both types of request?
In order to render the error page inside my CustomExceptionHandler, I use inside a utility method such a statement which identify the type of request (normal or ajax)
if (context.getPartialViewContext().isAjaxRequest()) {
ViewHandler viewHandler = context.getApplication().getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(context, errorPageLocation);
context.setViewRoot(viewRoot);
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
} else {
NavigationHandler nav = context.getApplication().getNavigationHandler();
nav.handleNavigation(context, null, errorPageLocation);
context.renderResponse();
}
By this way I can handle both request effectively in my CustomExceptionHandler
In the Wrapper you can use, if you want to detect the type of request:
context == null || !context.getPartialViewContext().isAjaxRequest()
Also recommend you to integrate the WrapperHandler with Deltaspike Exception Control
And finally a common way to redirect (working for both types of requests):
public static void redirect(FacesContext fc, String view) {
ExternalContext ec = fc.getExternalContext();
String path = ec.encodeResourceURL(fc.getApplication().getViewHandler().getActionURL(fc, view));
try {
ec.redirect(path);
} catch(Exception e) {
throw new RuntimeException();
}
}

Log to Elmah in async Task

I need some Elmah logging in a async task executing on my webserver. But when I try to log the error it fails because of the HttpContext.
var httpContext = HttpContext.Current;
Task.Factory.StartNew(() =>
{
HttpContext.Current = httpContext;
try
{
//Execute some code
}
catch (Exception ex)
{
//Generate some error for the user and log the error in Elmah
try
{
ErrorLog.GetDefault(HttpContext.Current).Log(new Error(ex));
}
catch(Exception ex)
{
}
}
});
To get the progress for the task I implemented some polling mechanism. Currently none of the errors are logged to Elmah which make it difficult to solve them.
Also providing the context as parameter doesn't work.
It doesn't work. I get an ArgumentException telling me the expected value doesn't fall within the expected range. With the following stacktrace:
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Web.Hosting.IIS7WorkerRequest.GetServerVariableInternal(String name)
at System.Web.HttpRequest.AddServerVariableToCollection(String name)
at System.Web.HttpRequest.FillInServerVariablesCollection()
at System.Web.HttpServerVarsCollection.Populate()
at System.Web.HttpServerVarsCollection.Get(String name)
at Elmah.ErrorLog.InferApplicationName(HttpContext context)
at Elmah.ErrorLog.GetDefaultImpl(HttpContext context)
at Elmah.ServiceContainer.GetService(Type serviceType)
at Elmah.ServiceCenter.GetService(Object context, Type serviceType)
at Elmah.ErrorLog.GetDefault(HttpContext context)
at Bis.Utilities.Log.ElmahErrorLog.TryLogError(Exception exeption) in D:\Users\A500535\Documents\Projecten\Biobank\Bis\src\Utilities\Log\ElmahErrorLog.cs:line 13
Below is one ugly hack that might get the job done. Essentially, it creates an Error object on a bogus Exception (the prototype) so that the context can be captured while the request is still in flight. Later, when the task started as a result of the request fails, another Error object is created off the actual exception that occurred and then the interesting and contextual bits are selectively copied off the earlier prototype. Unfortunately, the prototype Error has to be created whether or not an exception will occur.
// Create an error that will capture the context
// and serve as a prototype in case a real exception
// needs logging
var prototype = new Error(new Exception(), context);
Task.Factory.StartNew(() =>
{
try
{
// Execute some code
}
catch (Exception ex)
{
// Generate some error for the user and log the error in ELMAH
try
{
// Create a new error without contextual information
// but then copy over the interesting bits from the
// prototype capture at time of request.
var error = new Error(ex)
{
HostName = prototype.HostName,
User = prototype.User,
};
error.ServerVariables.Add(prototype.ServerVariables);
error.QueryString.Add(prototype.QueryString);
error.Cookies.Add(prototype.Cookies);
error.Form.Add(prototype.Form);
ErrorLog.GetDefault(null).Log(error);
}
catch(Exception)
{
}
}
});
When you start a new thread it doesn't get the HttpContext structure. Since Elmah logging requires the HttpContext data, it will fail.
See the following QA:
Elmah Does not email in a fire and forget scenario
for me this worked in a async task called with Task.Run:
Elmah.ErrorLog.GetDefault(null).Log(new Elmah.Error(new NotSupportedException("elmah logging test")));

Resources