We are trying to do input validation for requests using spring. We followed this tutorial: http://spring.io/guides/gs/validating-form-input/ but it doesn't seem to work. Looks like the #NotNull and / or #Valid are ignored for some reason.
For example, we have the following controller:
#RequestMapping(value = "/test", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.OK)
#ResponseBody
public void test(#RequestBody #Valid TestDTO testDTO, BindingResult result) {
this.validateDTO(result);
}
protected void validateDTO(BindingResult result) {
if (result.hasErrors()) {
List<FieldError> fieldErrors = result.getFieldErrors();
throw new FieldValidationException(fieldErrors);
}
}
TestDTO is the following class:
public class TestDTO {
#NotNull
private String test;
...
}
So we expect, that when POSTing a request without the test field, FieldValidationException will be thrown. However, this does not happen because result.hasErrors() = false for some reason.
Anyone know why this happens? And how to fix it?
EDIT: We tried to add to validator bean to our xml:
<beans:bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
But now we get the following exception we we start the server: java.lang.NoClassDefFoundError: javax/validation/ValidatorFactory.
We have the following gradle dependencies, to my understanding these versions should be compatible:
compile group:'javax.validation', name:'validation-api', version:'1.1.0.Final'
compile group:'org.hibernate', name:'hibernate-validator', version:'5.2.2.Final'
I thinks annotation #Valid should be before #RequestBody
Related
Note:
I read almost all stack post that i get when i search #valid vs #validated and could not find answer so i am posting this.
I am so confused what #Validated is doing here. If inputParms is not valid, it is throwing javax.validation.ConstraintViolationException and not even going inside code. But if I replace #Validated with #Valid No exception thrown and bindingResult.hasErrors() is catching it.
#RestController
#Validated // what is this doing ??
public class MyRestController{
#PostMapping(value="/my-data",produces= {MediaType.APPLICATION_JSON_UTF8_VALUE})
public ResponseEntity<?> doSomething(#Valid #RequestBody MyInputParam inputParms,BindingResult bindingResult){
if(bindingResult.hasErrors()) {
//do something here
}
}
}
So if i use #Validated, BindingResult is not useful at all ?
Or even simply how #Validated is different from #Valid
See Difference between #Valid and #Validated in Spring.
The problem in your particular case is that #Validated is mixed with #Valid. Options to fix this:
remove the #Validated declared on the class and use either #Validated or #Valid for the parameter
use either #Valid or #Validated in both places (class and parameter)
#RestController
public class MyRestController {
#PostMapping(value = "/my-data", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public ResponseEntity<?> doSomething(#Validated #RequestBody MyInputParam inputParms,
BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
//do something here
}
}
}
how to validate a spring boot rest endpoint with a String parameter, here is the sample of my endpoint.
#ResponseBody
#RequestMapping(value = "/getProfile", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getProfile(#RequestBody #Valid #NotEmpty String profileId){}
even thought i added #valid and #NonEmpty constraint its not getting validated.
but its working for a other classes. for eg,
#ResponseBody
#RequestMapping(value = "/saveProfiles", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> saveProfiles(#RequestBody #Valid PersistProfilesRequest request){}
here all the fields in PersistProfilesRequest class which have a constrain applied is validating properly.
validation is not happening for method parameter with java class like String and Map.
i am using spring boot 1.3.0 with hibernate validator.
how can i add validation to a rest endpoint with a string parameter?
EDIT
i am using junit and mockmvc to test the endpoint, below is the test case
#Test
public void testGetProfile() throws Exception{
String profileId = " ";
this.mockMvc.perform(post("/getProfile").content(profileId).accept(MediaType.APPLICATION_JSON).contentType("application/json"))
.andDo(print())
.andExpect(status().isInternalServerError());
}
You can try to annotate your controller class with #Validated. In my case it helped.
#Validated
#RestController
#RequestMapping(path = "/api/somethin")
public class LegacyRestController {
I have implemented rest service and want to do validation on it. In my class I have #RequestBody and #RequestParam. I want to validate both objects. I do that like this:
#Controller
#Validated
public class RestApiImpl {
#Autowired
ClassA classA;
#ResponseBody
#RequestMapping(value = "/classA",
method = RequestMethod.POST,
produces = {MediaType.APPLICATION_JSON_VALUE},
consumes = {MediaType.APPLICATION_JSON_VALUE})
public ClassAResponse getList(#Valid #RequestBody ClassARequest request,
#Min(value = 1, message = "At least 1 object is expected")
#RequestParam(value = "quantity",defaultValue = "3",required = false) String quantity) {
return (ClassAResponse) classA.executeService(request);
}
}
public class ClassARequest {
#NotNull
#NotEmpty
#Size(max = 6, message = "tes1")
private String value1;
public String getValue1() {
return value1;
}
public void setValue1(String value1) {
this.value1 = value1;
}
}
And the post return 500 internal server error and log:
09:49:21,240 INFO [STDOUT] 09:49:21,240 ERROR [[dispatcher]] Servlet.service() for servlet dispatcher threw exception
javax.validation.ConstraintDeclarationException: Only the root method of an overridden method in an inheritance hierarchy may be annotated with parameter constraints, but there are parameter constraints defined at all of the following overridden methods:
When I remove #Validated #RequestBody is validated and works fine but validation for #RequestParam is not working. And If I remove #Valid from #RequestBody validation for request is not working. How to configure this that it work for both #RequestBody and #RequestParam. Or there isn't any solution and only way is to move param quantity to request?
spring framework 3.2.18.RELEASE and hibernate-validator 4.2.0.Final (it can't be changed to newer version for spring and hibernate)
I am using Spring Boot 1.5.2.RELEASE and not able to incorporate JSR - 349 ( bean validation 1.1 ) for #RequestParam & #PathVariable at method itself.
For POST requests, if method parameter is a Java POJO then annotating that parameter with #Valid is working fine but annotating #RequestParam & #PathVariable with something like #NotEmpty, #Email not working.
I have annotated controller class with Spring's #Validated
There are lots of questions on SO and I have commented on this answer that its not working for me.
Spring Boot includes - validation-api-1.1.0.Final.jar and hibernate-validator-5.3.4.Final.jar .
Am I missing anything?
Example code ,
#RequestMapping(method = RequestMethod.GET, value = "/testValidated", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseBean<String> testValidated(#Email #NotEmpty #RequestParam("email") String email) {
ResponseBean<String> response = new ResponseBean<>();
response.setResponse(Constants.SUCCESS);
response.setMessage("testValidated");
logger.error("Validator Not called");
return response;
}
Below handler is never called when I send empty values or not well formed email address for email & control always goes to with in testValidated method.
#ExceptionHandler(ConstraintViolationException.class)
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseBean handle(ConstraintViolationException exception) {
StringBuilder messages = new StringBuilder();
ResponseBean response = new ResponseBean();
exception.getConstraintViolations().forEach(entry -> messages.append(entry.getMessage() + "\n"));
response.setResponse(Constants.FAILURE);
response.setErrorcode(Constants.ERROR_CODE_BAD_REQUEST);
response.setMessage(messages.toString());
return response;
}
ResponseBean<T> is my application specific class.
I had asked the question after more than two days of unsuccessful hit & trial. Lots of confusing answers are out there because of confusions around Spring Validations and JSR validations, how Spring invokes JSR validators, changes in JSR standards & types of validations supported.
Finally, this article helped a lot.
I solved problem in two steps,
1.Added following beans to my Configuration - without these beans , nothing works.
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
mvProcessor.setValidator(validator());
return mvProcessor;
}
#Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(HibernateValidator.class);
validator.afterPropertiesSet();
return validator;
}
2.Placed Spring's #Validated annotation on my controller like below,
#RestController
#RequestMapping("/...")
#Validated
public class MyRestController {
}
Validated is - org.springframework.validation.annotation.Validated
This set up doesn't affected #Valid annotations for #RequestBody validations in same controller and those continued to work as those were.
So now, I can trigger validations like below for methods in MyRestController class,
#RequestMapping(method = RequestMethod.GET, value = "/testValidated" , consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseBean<String> testValidated(
#Email(message="email RequestParam is not a valid email address")
#NotEmpty(message="email RequestParam is empty")
#RequestParam("email") String email) {
ResponseBean<String> response = new ResponseBean<>();
....
return response;
}
I had to add another handler in exception handler for exception - ConstraintViolationException though since #Validated throws this exception while #Valid throws MethodArgumentNotValidException
Spring #Validated #Controller did not mapped when adding #Validated. Removal of any inheritance from controller itself did help. Otherwise Sabir Khan's answer worked and did help.
I have the following Rest controller:
#RestController
public class DocumentSearchController_global
{
#InitBinder//("TestCustomAnotation")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new ChekAtleastOneValueValidator());
}
#RequestMapping(value = "/validator", method = RequestMethod.POST, produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
protected DocumentSearchResponse validatortest(#Valid #RequestBody TestCustomAnotation objDMSRequest, Errors e, BindingResult br) throws AppException
{
if(br.hasErrors())
System.out.println("ERRor");
if (e.hasErrors())
{
System.out.println("Got Error: "+ e.getFieldError());
}
DocumentSearchResponse objDocSearchResponse = null;
return objDocSearchResponse;
}
#ExceptionHandler
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public String handleMethodArgumentNotValidException(
MethodArgumentNotValidException error) {
System.out.println("ERROR-->>>>>>>>>>>>>>>>>>>>>>>>" +error.getMessage());
return "Bad request: " + error.getMessage();
}
}
And this is the bean where the request will be cast:
public class TestCustomAnotation
{
#ValidDocumentModifiedDate({"7Days", "30Days","60Days"})
String docModifiedDate;
#NotNull
String objectId;
#NotNull
String jobId;
Setter and GEtter
}
In the controller if I specify binder.setValidator(new
ChekAtleastOneValueValidator()); the contol will only go to
ChekAtleastOneValueValidator it will not check for #notnull
#ValidDocumentModifiedDate`
If I don't have binder.setValidator(new
ChekAtleastOneValueValidator()); then the control will check for
#notnull#ValidDocumentModifiedDate validation but not
ChekAtleastOneValueValidator.
My question is: is there a way in Spring to use Spring validation, custom annotation and #notnull annotation and get all the error of all the validation or spring allows to use only Spring validators?
Actually the question itself was wrong. I got the answer I use a Spring Validator class to validate all the request comming in and then use #validated in stead of #valid. I don't use annotation at the request anymore and let the class be a POJO. thats it problem solved