20

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.

reformed
  • 4,505
  • 11
  • 62
  • 88
gentlejo
  • 2,690
  • 4
  • 26
  • 31

4 Answers4

41

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.

Paulo Merson
  • 13,270
  • 8
  • 79
  • 72
Adam Gent
  • 47,843
  • 23
  • 153
  • 203
2

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.

GaryF
  • 23,950
  • 10
  • 60
  • 73
1

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.

Oh Chin Boon
  • 23,028
  • 51
  • 143
  • 215
0

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

danny.lesnik
  • 18,479
  • 29
  • 135
  • 200