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.
Related
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.
I'm using Google's Preconditions class to validate user's input data.
But I'm worried about where is the best point of checking user's input data using Preconditions class.
First, I wrote validation check code in Controller like below:
#Controller
...
public void register(ProductInfo data) {
Preconditions.checkArgument(StringUtils.hasText(data.getName()),
"Empty name parameter.");
productService.register(data);
}
#Service
...
public void register(ProductInfo data) {
productDao.register(data);
}
But I thought that register method in Service layer would be using another Controller method like below:
#Controller
...
public void register(ProductInfo data) {
productService.register(data);
}
public void anotherRegister(ProductInfo data) {
productService.register(data);
}
#Service
...
public void register(ProductInfo data) {
Preconditions.checkArgument(StringUtils.hasText(data.getName()),
"Empty name parameter.");
productDao.register(data);
}
On the other hand, the method of service layer would be used in just one controller.
I was confused. Which is the better way of checking preconditions in controller or service?
Thanks in advance.
Ideally you would do it in both places. But you are confusing two different things:
Validation (with error handling)
Defensivie Programming (aka assertions, aka design by contract).
You absolutely should do validation in the controller and defensive programming in your service. And here is why.
You need to validate for forms and REST requests so that you can send a sensible error back to the client. This includes what fields are bad and then doing localization of the error messages, etc... (your current example would send me a horrible 500 error message with a stack trace if ProductInfo.name property was null).
Spring has a solution for validating objects in the controller.
Defensive programming is done in the service layer BUT NOT validation because you don't have access to locale to generate proper error messages. Some people do but Spring doesn't really help you there.
The other reason why validation is not done in the service layer is that the ORM already typically does this through the JSR Bean Validation spec (hibernate) but it doesn't generate sensible error messages.
One strategy people do is to create their own preconditions utils library that throws custom derived RuntimeExceptions instead of guava's (and commons lang) IllegalArgumentException and IllegalStateException and then try...catch the exceptions in the controller converting them to validation error messages.
There is no "better" way. If you think that the service is going to be used by multiple controllers (or other pieces of code), then it may well make sense to do the checks there. If it's important to your application to check invalid requests while they're still in the controller, it may well make sense to do the checks there. These two, as you have noticed, are not mutually exclusive. You might have to check twice to cover both scenarios.
Another possible solution: use Bean Validation (JSR-303) to put the checks (preconditions) onto the ProductInfo bean itself. That way you only specify the checks once, and anything that needs to can quickly validate the bean.
Preconditions, validations, whether simple or business should be handled at the filter layer or by interceptors, even before reaching the controller or service layer.
The danger if you check it in your controller layer, you are violating the single responsibility principle of a controller, whose sole purpose is to delegate request and response.
Putting preconditions in service layer is introducing cross cutting concerns to the core business.
Filter or inceptor is built for this purpose. Putting preconditions at the filter layer or in interceptors also allow you to “pick and match” rules you can place in the stack for each servlet request, thus not confining a particular rule to only one servlet request or introduce duplication.
I think in your special case you need to to check it on Service layer and return exception to Controller in case of data integrity error.
#controller
public class MyController{
#ExceptionHandler(MyDataIntegrityExcpetion.class)
public String handleException(MyDataIntegrityExcpetion ex, HttpServletRequest request) {
//do someting on exception or return some view.
}
}
It also depend on what you are doing in controller. whether you return View or just using #ResponseBody Annotation. Spring MVC has nice "out of the box" solution for input/dat validation I recommend you to check this libraries out.
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/validation.html
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'm trying to intercept Spring Controller calls which are annotated, similar to:
#RequestMapping("/my/page")
#AccessRestriction(module = Module.Audit, action = AuditActions.Log)
public ModelAndView myPage() {
// pls type teh codez
}
At this point I want to access both the values of the #AccessRestriction method, the HttpServletRequest object to check if the values match the restrictions and the HttpServletResponse object in order to send a redirect , if applicable. Being able to throw an exception might be suitable as well.
I've looked into Interceptors but they don't offer access to the method, just the handler. What are my options of achieving this?
My suggestion would be to decouple the two concerns, one to check the annotation and throw an exception, another to catch that exception and translate it into a redirect.
The first concern could be done using the Auto-proxy facility, which would apply an AOP-style interceptor to any invocations on your controller objects. They would check for the annotation, validate the invocation, and throw a custom RuntimeException is the conditions are violated.
You could then have a custom HandlerInterceptor which checked for this exception in the afterCompletion method, sending a redirect via the response object if it's present.
I'd like to be able to create a base controller in my Spring app that, among other things, determines if a user is a registered user or not. This base controller, following the template design pattern, would contain an abstract protected method that controller subclasses would implement.
The abstract method would have passed to it an instance of User, registered or otherwise. However, I have no idea how I would do this since it seems that by using controllers purely using the #Controller annotation each controller is free to define their request handling method however they like.
Would creating some sort of user service class that is injected into each controller and used to validate a user be one way to get around this? This begs the question (at least for me) how does such a controller get a hold of a HttpServletRequest or the Session object?
Thanks.
Define an abstract BaseController, with no annotations
Define concrete and abstract methods
Call these methods from subclasses (which are annotated with #Controller) whenever needed.
I think the Base Controller is not a good idea if the only code it is to have is for UserAuthentication...instead use Spring security. This is the best option.
Alternatively, you can have methods like this...take a look at the Spring reference..
#Controller("loginController")
public class LoginController {
#RequestMapping(value="/login.do", method=RequestMethod.POST)
public String login(Model model, HttpServletRequest request) {
String userIdFromRequest = (String)request.getParameter("userId");
String password = (String)request.getParameter("password");
boolean verified = ...send userIdFromRequest and password to the user service for
verification...
if (verified){
request.getSession().setAttribute("userId", userIdFromRequest);
}
}
//More Methods
}
Did it help?
-SB
The basic problem is that annotational bootstrapping is not polymorphic. I found this paper useful: http://sanguinecomputing.com/design-pattern-for-hierarchical-controller-organization-with-annotational-configuration-spring-mvc-3/