Spring 3 - Annotation based Bean Validation

2019-07-28 05:03发布

I am building a REST API. Its made up of a Resource ( @Controller ) which returns a response 204 even when one of the mandatory field is not present.

I am using Spring 3.1, validation-api (1.1.0.Final) & Hibernate-validator(4.3.0). Not sure if Hibernate-validator plays any role here.

 <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>4.3.0.Final</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.validation</groupId>
                    <artifactId>validation-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

I have a spring controller @Controller and a Bean with @Component

  @POST
        @Consumes(value = {MediaType.APPLICATION_JSON})
        @Produces(value = {MediaType.APPLICATION_JSON})
        @RequestMapping(method = RequestMethod.POST)
        public Response addUserData(@Valid @RequestBody UserData userDataRequest) throws ResourceException {
    ...
    }

My UserData bean has

  @Component 
    public class UserData {

        @NotNull(message = "user ID should not be null")
        @Min(value = 1, message = "user ID should not be empty")
        @Max(value = 20, message = "user ID should have length of more than 20")
        @Pattern(regexp="[A-Z][a-z]+", message = "Only Alphanumeric characters allowed")
        private String userId;

    }

My validations are not getting executed. When I dont pass "userId", there is no error thrown. What am I missing here ?

3条回答
等我变得足够好
2楼-- · 2019-07-28 05:27

Maybe passing to your "addUserData" method, a bindingResult Object, so you can test for and retrieve validation errors. here is an example of how to use it : Validation form input

查看更多
劳资没心,怎么记你
3楼-- · 2019-07-28 05:28

You must have the following about the infrastructure

@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    ...

    @Autowired
    private ValidatorConfig validatorConfig;

    ...

    @Override
    public Validator getValidator() {
        return validatorConfig.localValidatorFactoryBean();
    }

    ...

}

Where validatorConfig comes from

@Configuration
public class ValidatorConfig {

    @Autowired
    private ReloadableResourceBundleMessageSource rrbms;

    @Bean
    public LocalValidatorFactoryBean localValidatorFactoryBean(){
        LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
        localValidatorFactoryBean.setValidationMessageSource(rrbms);
        return localValidatorFactoryBean;
    }

}

And finally (I suggest you consider put the error messages in a .properties file, such as validation.properties how shown below)

@Configuration
public class MessageSourceConfig {

    @Bean(name="messageSource")
    public ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource() {
        ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
        resource.setBasenames("classpath:/com/manuel/jordan/somethinga",
                              "classpath:/com/manuel/jordan/validation/validation");
        resource.setDefaultEncoding("UTF-8");
        return resource;
    }

}

Some considerations or suggestions:

  • change @Valid to @Validated (see the API for the @Validated)
  • Remove the @Component for UserData (that represents a kind of entity right?). Remember that for that class each instance is unique and any bean managed by Spring is Singleton.
  • put the error messages in a .properties file
  • from where come the @POST, @Consumes and @Produces annotations?. They are not in the Spring API

Addition 01 about your comment:

  • Yes, you must use @EnableWebMVC. It indicates to Spring create some special beans internally for the web environment. See the @EnableWebMVC API. Is very important that annotation. Even for Rest I use that annotation.

  • About the Rest annotations, use the Spring annotations. Such as @RequestMapping and new 'variations' such as @GetMapping, @PostMapping etc.. That annotations contain the produces and consumes attributes. I have never seen your approach about mixing two sets of annotations from Rest.

Addition 02

The WebMvcConfigurerAdapter class represents the XML configuration file about all the Spring MVC infrastructure

Therefore for XML

  • @EnableWebMvc is equivalent <mvc:annotation-driven/>
  • About validation it should be: <mvc:annotation-driven validator="beanNameValidator" /> where the validator attribute according with the .xsd says:

Attribute : validator The bean name of the Validator that is to be used to validate Controller model objects. This attribute is not
required, and only needs to be specified explicitly if a custom Validator needs to be configured. If not specified, JSR-303 validation will be installed if a JSR-303 provider is present on the classpath.

beanNameValidator according with my @Bean seems should be localValidatorFactoryBean

查看更多
手持菜刀,她持情操
4楼-- · 2019-07-28 05:43

I ultimately ended up using Jersey Bean Validation, instead of Spring. This is because rest of my code is using Jersey anyways. To make this work I just imported Jersey Bean Validation jar and added a small change to web.xml. Validations are now working.

Thank you @Manual Jordan. I will upvote your answer, since it gave me the right clues.

<!--  jersey beanValidation  -->
       <init-param>
           <param-name>jersey.config.beanValidation.enableOutputValidationErrorEntity.server</param-name>
           <param-value>true</param-value>
       </init-param>
查看更多
登录 后发表回答