问题
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 ?
回答1:
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
forUserData
(that represents a kind of entity right?). Remember that for that class each instance is unique and anybean
managed by Spring isSingleton.
- 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 theproduces
andconsumes
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 thevalidator
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
回答2:
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>
回答3:
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
来源:https://stackoverflow.com/questions/39841799/spring-3-annotation-based-bean-validation