Edit: I have looked into Spring 3's #ExceptionHandler annotation and combining this with Option 1 below looks to be a pretty clean solution.
See http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers
I also found this to be a good read: http://blog.decaresystems.ie/index.php/2006/04/07/difficult-choices-in-handling-exceptions-in-enterprise-java-applications/
I have been developing using the Spring MVC framework for some time now however I am struggling to come up with a 'nice' way to pass errors that are raised in the service layer back to the JSP.
Basically, I don't believe that business logic (beyond "this field is mandatory") should be in the Validators, especially any logic that requires access to the DB. So, what I have been doing is placing further, more complicated validation and business logic in the service layer.
For example, lets say I have a page that allows a user to buy a Book. They click "Purchase" on the JSP and the controller calls the service to make it all happen... Now, what happens if the service sees that they have insufficient funds - how do I get this message back to the JSP so a nice little "Insufficient funds" message can be displayed to the user? I have considered two ways and I'm not sure which is correct...
Option 1: Exceptions
The first way I thought was to raise an exception in the service layer, trap it in the controller and add a message to the BindingResult.
Service:
public void pay(Book book) throws InsufficientFundsException {
// Some logic goes here, which ends up throwing the above exception
}
Controller:
public ModelAndView(#ModelAttribute("book") Book book, BindingResult errors) {
try {
pay(book);
} catch (InsufficientFundsException ex) {
errors.reject("insufficient.funds");
}
return new ModelAndView(blahblahblah);
}
Option 2: Pass BindingResult to Service layer
The second way was to pass the BindingResult object to the service layer and raise further errors against it.
Service:
public void pay(Book book, BindingResult errors) {
// User has insufficient funds, so...
errors.reject("insufficient.funds);
}
I can see problems with both of these ways. Option 1 feels awkward because not only do I have to catch the exception, I then have to add the error to the binding result so it feels like I'm doing the same thing twice. And Option 2 seems to bind the service layer too tightly to the controller.
Finally, I realise there is the SimpleMappingExceptionResolver that could be used in conjunction with Option 1, but I'm not sure how appropriate it is (perhaps I have not seen a proper example?). In the above example, lets just say for argument's sake that I'd like the user returned to the original form with a red error above the form, not redirected to an entirely different page. The SimpleMappingExceptionResolver seems to me to be useful when you want to redirect a user to a standard error page when a certain exception is raised (which is not quite what I want to know how to do).
Java uses exceptions to naturally handle this kind of thing. In the end it generally simplifies your logic and reduces the chance of making a mistake by forgetting to check that something had an error. You are also able to move error logic out of the main flow of the code.
I don't see why the case you present is different from any other case where I would use exception handling to deal with errors.
Related
I have my regex patterns stored like:
private final Pattern regex1...
private final Pattern regex2...
As fields in my view. I have a validation check method in the view class which throws an exception to the controller when validation fails. Is this valid in MVC or not?
public void validation() throws Exception{
if(regex failed){
throw new exception("...");
}
...
}
It is always somehow up to you how you splitt the code, if there is a good reason behind your decision.
However, I would prefer to put the validation methode at least into the controller, because it looks like it will be triggered by an actionevent from a button. (Events should be handled in the controller)
What you can do, is to create a methode in your view, which shows an error message in the gui, if your validation has failed.
All this implice that also the regex is saved in the controller.
It is not good practice. Logic has to be moved into dedicated layed.
Best way is creating dedicated validator class OR create validation method of a service (if some operations like search in DB have to be done during the validation). In that way validator class can be well covered by unit tests.
#Autowire
UserInputValidator userInputValidator;
public void validation() throws Exception{
if(userInputValidator.validate(param1, param2)){
throw new exception("...");
}
...
}
While we're talking about MVC, it's always best to consider that as the app evolves, "input" may come from more than one place eventually.
User
service call
file
other
In this case we wouldn't want to build the validation mechanisms into the View or the Controller, but into another component that the Controller would just use/integrate with.
Validation can be a cross cutting concern (but not always)
Ideally we would want to avoid duplicating the validation of incoming data especially if the data is always valid in the same way. Duplicating the validation can cause inconsistencies. Imagine data being invalid from the UI, but the same data being considered "ok" from a service call.
There are a number of ways that you can separate the validation logic from your view:
Annotate beans for JSR-303
Create custom validation annotations (abstracting your defined regex's) and/or use libraries that provide some basic ones
Dedicated service
Resolving validation failures
I can see that you're throwing an exception when you have a validation failure. Consider the awesome article by Martin Fowler. The suggestion is that a validation failure can be expected and is not an Exception case. He also suggests a notification approach.
Another advantage of a notification approach is that your dedicated validation service/tier can support multiple validation failures. This would allow your view to highlight multiple fields or your API to return a list of failures.
We are creating an app for iOS and have chosen Java running on Google App Engine for the backend. We use Google Cloud Endpoints to generate client library for iOS. We are relatively new to most of this and we are not quite sure how the backend should respond after an error based on user action. For example we have a method for creating a custom user, which should return the user-object if the save was successful. But there may be conditions that are not met and if so the app must get an appropriate response and handle present to the user. This is how we thought of doing it:
#ApiMethod(name = "createUser", httpMethod = HttpMethod.POST)
public User createUser(User user) throws BadRequestException
{
if(!validateUsername(user.getUsername()))
throw new BadRequestException("Invalid characters in username");
//Do stuff and save user
.........
return user
}
And then maybe throw a custom exception derived from ServiceException if the username exists etc. Is this a reasonable way to do it? I just feel like it's a little brutal to be throwing exceptions every time something like this happens. It also also appears in the log which could make it difficult to distinguish between actual application errors that needs to be fixed.
I believe your approach for throwing error 400 (BadRequestException) is fine in this case.
Additionally, Endpoints also provides other exceptions like UnauthorizedException (401), ForbiddenException(403), etc. Check out the list.
You can also have custom Exceptions that you can map to other HTTP codes that Endpoints supports. So your approach to have a Custom Exception is fine but do keep in mind that it will need to map to one of the HTTP codes eventually.
When it comes to parsing out the response in the client and doing some custom processing based on the error, it could get trickly, since all the control that you have to set the specific details are limited to the message attribute. So you will need to hack out the message format in such a way that you can parse and figure out what to do.
Note: Skip to the two updates at the bottom to find the answer.
I have a Spring (3) MVC web application running on Tomcat. This web application serves as a UI for a remote WSDL service layer. I have written a custom AuthenticationProvider to authenticate to this remote service. That works fine. My issue is this:
Each controller method, more or less, results in a remote call to this remote WSDL. My plan was to not have Tomcat or Spring expire sessions, but wait until the remote server tells me that the session has expired on that end and then re-route the user to the login page, telling them that their session has expired.
BaseController, from which all other Controllers extend, has this method:
protected void addBindingErrors(HttpSession session, MTAResponse response, BindingResult binding, boolean allowDefault) {
switch (response.getResponseCode()) {
case SESSION_EXPIRED:
UserInfo nfo = (UserInfo)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
SecurityContext context = SecurityContextHolder.getContext();
System.out.println("token in handle session expired: " + nfo.getMtaCoreSessionId());
if(session != null){
System.out.println("invalidating session");
session.invalidate();
//1. throw new SessionAuthenticationException("session expiredd");
ModelAndView mav = new ModelAndView("error");
throw new ModelAndViewDefiningException(mav);
//3. something else?
}
context.setAuthentication(null);
break;
case INVALID_PARAMETER_VALUE:
binding.addError(...);
break;
case ACCESS_DENIED:
binding.addError(...);
break;
default:
if(allowDefault){
binding.addError(...);
break;
}
}
}
I have found a couple solutions, but I'm new to Spring so I don't know if they're the "right" ones or if there are issues lurking in them.
(a bit of a cheat) In the BaseController.addBindingError() method, if a remote session expired code is received, throw a SessionAuthenticationException
Pros: this works, automagically re-routes the user to login page (I think because I have authentication-failure-url set?)
Cons: I’d have to change the signature of addBindingError to throw this kind of exception.
Con: I’d also have to change the signature of every controller method to throw this kind of exception, since they all make remote service calls to WSDL.
Con: the user doesn’t know why they’re back at the login page and I’m not sure there is a way to let them know
In the BaseController.addBindingError() method, if a remote session expired code is received, throw a ModelAndViewDefiningException
Pros: this works, automagically re-routes the user to appropriate error page, which can contain a link to the login page
Pro: we can explain to the user on this page how they got here.
Cons: I’d have to change the signature of addBindingError to throw this kind of exception.
Con: I’d also have to change the signature of every controller method to throw this kind of exception, since they all make remote service calls to remote WSDL
In the BaseController.addBindingError() method, if a remote session expired code is received, throw a MyCustomRemoteSessionExpiredException and then catch it in a SimpleMappingExceptionResolver
Pros: this works, automagically re-routes the user to appropriate error page, which can contain a link to the login page
Pro: we can explain to the user on this page how they got here.
Cons: I’d have to change the signature of addBindingError to throw this kind of exception.
Con: I’d also have to change the signature of every controller method to throw this kind of exception, since they all make remote service calls to remote WSDL
Numbers 2 and #3 have the same pros/cons. #3 seems like it might be a more "correct" solution. Does anyone have any feedback, suggestions, or alternatives?
Update 5/30/2013:
I think I may have found what I'm looking for and I think it will require me to upgrade to the latest spring 3.2. #ControllerAdvice a la this article here:
http://www.javacodegeeks.com/2013/03/exception-handling-with-the-spring-3-2-controlleradvice-annotation.html
From BaseController.addBindingError, I'll throw MyCustomException (which extends RuntimeException so that the method signatures will not need to declare a throws statement) and handle that in a new #ControllerAdvice class (which will contain an #ExceptionHandler method).
Can anyone confirm or deny this approach?
Any better suggestions?
Thanks!
Update 12/3/2013 **
This question would have been much better titled like so: "How do I implement site-wide exception handling in a spring MVC architecture?". So, I'm changing the title (the former title was based on my misunderstanding that the interaction with the remote SOAP server had anything to do with my real question). I implemented the solution discussed in May and it has been working very well. I have even extended the #ControllerAdvice class to handle other RuntimeExceptions.
I don't like throwing exceptions for some reason, maybe because of the performance hit I don't know, wondering if I should re-think this issue.
Should my service layer (uses Dao's + business logic etc.) be throwing exceptions?
public ModelAndView createProduct(#Valid ProductForm productForm, ..) {
ModelAndView mav = new ModelAndView(...);
if(bindingResult.hasErrors()) {
return mav;
}
// throw exception if user doesn't have permissions??
productService.create(product, userPermissions);
}
So my options in the create method of the ProductService:
if the user doesn't have permissions, throw an exception
return some sort of a Response object that will have the new product Id if it was a success, along with a success/failure flag and a error collection.
Things to keep in mind:
I may re-use this service layer in a non-web app, also in a restful web service.
What is considered best practice?
Depends of what you mean by service and exception, but in the context you've got I'll assume a java exception from a HTTP endpoint.
The answer is no. Services should expose errors in a general way. In the case of Restful service, errors should be propagated as HTTP status with error codes. The service shouldn't leak implementation details to consumers. It's a natural boundary.
The Consumer should handle those error situations and decide the most appropriate what to communicate that. It may well choose to generate an exception. But these exceptions are disjoint from the original issue/eception that caused the service to return an error code.
Going further I would say #yahir is right in what he says also. HTTP service would expose HTTP errors, and it may well just be using another service underneath that returns another kind of errors, but it's job will be to handle or map them appropriately.
Ask yourself what other options do you have, sometimes exceptions are necessary. The only other thing you could do is return a status of failure or success and handle appropriately.
I'd say the service layer should behave just like any other method exposed to client code. After all, that's exactly what it is.
Clients that will use it through RPC, will expect exactly this behavior.
Other cilents, such as REST, should anyway access the services layer through some other wrapping layer (e.g. Controller layer). One of this wrapping layer duties is transforming the response to be client-consumable.
I am working on a legacy project, converting it to annotation based MVC. I am noticing alot of times in the onsubmit method where the following pattern is followed:
public ModelAndView onSubmit(Command command) {
try {
service.doSomeBusinessLogic(command);
}
catch (ServiceException) {
//return one type of model and view here
}
//return another type of model and view here
}
Intuitively to me, this exception handling placed here is wrong, but I am unsure as to what alternative solutions spring gives me? Any ideas or is this not an anti-pattern like I am thinking?
The best practice for unrecoverable exceptions is to show an error page. Configure that in web.xml. Spring's default exception handler will take care of setting the response status to 500, so a 500 error page will be displayed.
If the exception is recoverable, then the above approach is fine - there is a specific action to take, and it goes in the catch clause.
But you should decide which exceptions are recoverable and which should just lead to an error page.