I implemented an ExceptionFilterAttribute class and registered it in the WebApiConfig class.
The action filter works good and handles any exception happened in any action but the problem is: when an exception happens while in any of the controller properties the action filter not handle this exception
[NotImplExceptionFilterAttribute]
public class AnyController : APIController
{
private readonly ModelDBContext _db = new ModelDBContext();
//some actions
}
in the mentioned example, the constructor of ModelDBContext contains some logic which may cause exception. This exception will not be handled by the ExceptionFilterAttribute. Why??? And how to handle it?
basically, simple answer is simple: exception filter is action-level filter.
Check this diagram first: http://blogs.msdn.com/b/kiranchalla/archive/2012/05/06/asp-net-mvc4-web-api-stack-diagram-currently-in-development.aspx
Then navigate to ApiController, line #232. There you can see that if there is at least one filter then ExceptionFilterResult will be used to wrap end action result.
Inside ExceptionFilterResult there is simple try-catch, if exception is thrown then all registered exception filters are called.
So, short summary of everything written above:
1) exception filter is not responsible for handling errors above controller's action
2) exception filter does not exist at the moment when controller's instance is created by DI container and when your ModelDBContext throws an exception
I hope this answers your question.
The problem is scope.
When the controller is instanced the private class member _db will initialize before any class methods execute. This is a CLR behavior.
Thus, an action filter will not catch an exception incurred during construction/initialization of your controller object, such as a failure to construct a ModelDBContext instance.
One solution is to create and dispose of ModelDBContext instances on every request, and some may argue this is the proper approach if you intend to gracefully handle (or log) connectivity failures (whether it's to a DB or a back-end service.)
You may also find IServiceLocator and a framework such as 'Unity' or 'Ninject' useful so that you're not "hardcoding" new ModelDBContext(); statements everywhere, but that's another subject. The root cause is you're initializing outside of the scope of an action method, action filters won't catch it.
Related
I would like some advice on how to achieve the following. I'm not providing code, as my problem is theoretical, but upon request I can. So this is the situation:
I have multiple controllers, each can throw XYException
I have a #ControllerAdvice class, in which I have an #ExceptionHandler watching for XYExceptions. When it happens, it prints out "XY".
In one (and only one) controller, when XYException is thrown, I want to do some additional task (let's say, do something that only that controller can do), and then I want the exception to be "passed on" to the global handler mentioned above.
I know I can catch the exception, do the desired task in catch block, and then re-throw the exception so the global handler can catch it, but what if I have 23 methods in the controller potentially throwing XYExceptions, and I do not want to put try-catch blocks in all 23 methods.
What is the clean Spring way of achieving this?
You could use AOP to achieve that. You'd write a method that intercepts the method inside that controller and when they throw an exception, you're aop method would run and you can do your stuff and then the exception would go in your handler class.
To achieve that you should have a class and anotate it with #Aspect and #Component
then have a method anotated with #AfterThrowing and setting the pointcut which will intercept when the given method throws an exception.
Look into Aspect Oriented Programming and Aspectj for more info.
The easy way to handle this case in ControllerAdvice is checking the stacktrace where the exception originated.
#ExceptionHandler(Exception.class)
public String handleExc(HttpServletRequest req, HttpServletResponse res, Exception e) {
if ( /*Have all null and safe check */ e.getStackTrace()[0].contains("MyController")) {
// Do your exception handling
}
}
I have a SpringBoot based REST Api structured as follows :
Rest Controller -> Service -> Repository
and I'm wondering how exactly to handle exceptions "properly".
For instance, let's say someone calls a /myresources/{id} endpoint with a non-existant {id}. The call gets delegated to the service which in turns tries to get the MyResource from the Repository. It fails and returns null. The Service then throws a MyResourceNotFoundException.
Now I want a specific format for my REST errors so I have a #ControllerAdvice ResponseEntityExceptionHandler which handles the custom serialization of these exceptions (#ExceptionHandler(MyResourceNotFoundException.class)).
Fine.
But this is going to result in a lot of handling/translation for each different custom exception. So I thought I could generify this by adding HttpStatus codes and messages to a base abstract exception class which MyResourceNotFound and others would extend and override. Thus the ResponseEntityExceptionHandler would simply handle building my custom error DTO in a standard way.
But then I realised that I'm adding REST concepts to an exception thrown at the service level. These shouldn't be aware of such notions...
Maybe I should catch the MyResourceNotFoundException in the Controller and throw another layer-specific exception which contains the appropriate messages and HttpStatus etc. in order to handle this generically in the ResponseEntityExceptionHandler...
What are your thoughts on this ?
You can generalize the exception as XYZMicroserviceException.
class XYZGenericException extends Exception{
String message;
custom error details....
XYZgenericException(errorMessage, custom error Details..){
this.message=errorMessage;
.......
}
}
and you can surround the suspected call which would lead to exception with try block and raise the generic exception in catch block that can be handled in global exception handler.
try{
xyz.abcMethod() // may give some exception
}
catch(Exception e){
throw new XYZGenericException(.........)
}
In the exception handler class with #restcontrolleradvice you can annotate the methods with the type of specific exception class to be handled.
This should be a common problem with a solution but I haven't managed to find it anywhere.
I am defining a global exception handler using #ControllerAdvice, I define a new ModelAndView and redirect to my error page. Works great EXCEPT for the fact that now we want to add a link to go back to the original page which of course could vary depending on where the error originated.
What I want to do is to store some kind of context information about the controller that generated the error, for instance if it's MyController then I can access a value via MyController.EXCEPTION_REDIRECT_URL and generate the appropriate link.
I find a lack of context information in the Exception handler rather limiting.
Make a custom exception class and pass your context in the exeption.
class MyException extends Exception {
private MyController c;
MyException( String msg, MyController c ) {...}
...
}
I do feel like there's a better way, probably revolving around a request context object. But this would do exactly what you want.
I have an MVC3 app with a simple Log service. All my services are invoked using Microsoft's Unity Dependency Injection container.
For most methods, I ignore exceptions; they're caught in a top-level error handler, which categorizes them and decides whether or not to log them, generates the HTTP response I desire and calls an action method on the error controller to return my custiom error page.
Sometimes, tho, I don't want to do that; I want to handle the exception where it happens, e.g. in my controller. In that case, I want to log the error before substituting an appropriate default value and continuing with the controller's logic.
I did that in one place: in my controller, I added:
var logService = DependencyResolver.Current.GetService<ILogService>();
try { /* something indeterminate */ }
catch ( Exception ex ) {
logService.LogException(category, ex);
/* do something else instead */
}
Now I want to do it a second time in that controller (as it happens, later in the same method). As soon as I do this again, I see it's time to refactor, as I'm repeating myself.
What's the best way to make my logger available to my controllers? My controllers all inherit from a custom ControllerBase; my first thought is to add it to the ControllerBase's constructor. BUT:
currently I don't have a constructor in the ControllerBase,
I'm a bit worried that referencing the DI container in the
controller breaks the isolation of the controller, negating the
value of DI to begin with, and
I don't think I can pass the
logger in to the ControllerBase's constructor, because (as I
understand it, pls correct me if 'm wrong) controllers can only have
parameterless constructors, so there's no way to pass anything to
them.
Where's the proper place to make the service available to all my controllers?
Place it in your constructor. You can even place it in a BaseController.
Ideally, you will use Dependency Injection and have it come in on your controller. Controllers can have parameters if your IoC container supports it.
private readonly ILogService logService;
public MyController(ILogService logService)
{
this.logService = logService;
}
I wouldn't add it to the base controller for the sole reason that it sounds as if you only use it sporadically. I would add it as Daniel White suggests in the constructor of the controller. If it's looking as though you use it in most controllers, then I'd consider moving it to the base.
I can't figure out how to handle more than one kind of exception by #ExceptionHandler.
I need to programmatically deal with these exceptions, for this I'd need a shared reference. Is this done via this reference "Exception ex" ? I don't think so, cause the exception is not caught like this, how would I do it then ?
I can't put all the exception references as arguments to the handler method, it wouldn't make sense, it can't be programmatically dealt with. I need a shared reference so that I could use "instanceof" on it or just send it somewhere else as a general "Exception"
#ExceptionHandler({DescriptionCstOrderException.class, SpecializationCstOrderException.class, NoUploadFileException.class,
DeadLineCstOrderException.class, DocumentCstOrderException.class, CommentCstOrderException.class})
public String handleFormException(Exception ex, ActionRequest actionRequest) {
logger.error(ex.getMessage());
SessionErrors.add(actionRequest, ex.getClass().getName());
return "mainOrderForm";
}
Additional question: what if I wanted to handle org.springframework.web.multipart.MaxUploadSizeExceededException, that is not thrown from any method of the handler? Because #ExceptionHandler catches only exceptions that are thrown from one of the handler methods.
The exceptionHandler method could be placed into some extended parent controller but if one uses only defaultAnnotationHandlerMapping... ?
Appreciate any help, I'm going crazy, this is very frustrating....
The #ExceptionHandler value can be set to an array of Exception types.
The implementation of using exception array as mentioned in Spring documentation will be like:
#ExceptionHandler({
NotFoundException.class,
MissingServletRequestParameterException.class
})
The #ExceptionHandler value can be set to an array of Exception types. If an exception is thrown matches one of the types in the list, then the method annotated with the matching #ExceptionHandler will be invoked. If the annotation value is not set then the exception types listed as method arguments are used. See the documentation for details.
Your question is rather confusing but your exception handler method will only handle one exception at a time. It will not catch multiple exceptions and then pass both of them into your handleFormException() method. If you need to handle these exception types differently then you should create an exception handler method for each one, specify an argument of that specific Exception type to your method, and then do the appropriate handling. For example:
#ExceptionHandler(DescriptionCstOrderException.class)
public String handleDescriptionCstOrderException(DescriptionCstOrderException exception, ActionRequest actionRequest) {...}
#ExceptionHandler(SpecializationCstOrderException.class)
public String handleSpecializationCstOrderException(SpecializationCstOrderException exception, ActionRequest actionRequest) {...}
// and so on...
Please refer to the Spring documentation for further information:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-exceptionhandler