Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing errors back to the view from the service layer

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).

like image 264
Ben J Avatar asked Jul 11 '10 21:07

Ben J


1 Answers

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.

like image 188
TofuBeer Avatar answered Oct 02 '22 15:10

TofuBeer