Validation in Spring MVC in Controllers or Service

2020-02-23 07:09发布

For quite some time I try to figure out where validation of user input should take place in a Spring MVC application. In many online blogs and tutorials I basically read that a controller should validate the users input and, if invalid, respond to the user by showing a page containing the error message. My current understanding of the Spring and Spring MVC layering system, however, is that a Controller is a only shallow interface between the application logic (service layer) and the "web world", allowing usage of the service layer from the web. Also, as far as I can see, Spring MVC does only provide reasonable tools for validation in a Controller.

If now validation takes place in a Controller, if at some later point I want to untie the application logic from the "web world", validation logic must be reimplemented in the new environment (e.g. a desktop application using Swing). In my opinion, the ability to decide which operations are "valid" on domain objects, and what "valid" states such objects may have, is core part of the service layer, and not the concern of some other part of the application (e.g. Controllers).

In this context, why is it "good practice" to place input validation logic in the controller layer and not the service layer?

2条回答
不美不萌又怎样
2楼-- · 2020-02-23 07:11

In one of our previous projects, we had huge forms with very complex logic which meant a lot of validating code. So we used a third kind of solution. For every controller, we autowired a helper class. Example:

myview <-> MyController <- MyService <- MyDAO
                 ^
                 |
               MyHelper

Controllers handled the view resolving.
Services handled mapping from dto-s to model objects for view and vice versa,
DAO-s handled database transactions and,
Helpers handled everything else including validation.

If now someone would have wanted to change the frontend from web to something else, it would have been a lot easier and at the same time, we didn't over-bloat the service implementation classes.

查看更多
爷的心禁止访问
3楼-- · 2020-02-23 07:28

A common approach is to do validation on both places. But if you are talking about @Valid, from my experience it is nicer to put on Controllers level.

It also depends what kind of validation logic we are talking about. Let's say you have a bean:

@Data
public class MyBean {
    @NotNull private UUID someId;
    @NotEmpty private String someName; 
}

It would make sense for this bean to be annotated with @Valid on the controller level so it doesn't even reach the service. There is no benefit to putting the @Valid on the service method, because why would you propagate it further while you can immediately in the controller decide if it is that kind of valid or not.

Then there is a second type of validation: business logic validation. Let's say for the same bean that the someId property is a timeUUid and its timestamp needs to be at most 2 days after some event occurred, in other case, the bean should be discarded by the service.

That seems like a business logic validation case, because by just looking at the bean, you wouldn't be able to validate it, unless you apply some logic to it.

Since both approaches to validation actually validate different things, it is obvious to see that each of your MVC components - Model, View and Controller, do their own validation and it should be reasonable about what it validates without introducing dependency to the other components.

As for showing the error to the user, yes, the Errors object is indeed intended to be used for bean validation at controller level, but you can design some filter that catches exceptions on any level and then pretty formats it for the user. There are many approaches to it, and I am not sure if Spring prescribes that any is better than the other.

Depending on different resolve mechanism (as in, for example, jstl or jackson or something else), you would probably be inclined to deal with validation in a different way. For example, a traditional jstl view resolver would nicely work with a contraption that uses Errors, while a jackson resolver would probably work nicer with a combination of @ResponseBody and some filter that catches errors and puts them in a predefined error part of the response object.

查看更多
登录 后发表回答