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
}
}
}
Related
I'm learning java and spring boot and I am trying to validate a controller parameter which was bound from json.
I've got simple Entity:
#Getter
#Setter
class Resource {
#NotBlank
String uri;
}
Which I want to persist through the following controller:
#BasePathAwareController
public class JavaResourcePostController {
private final ResourceRepository repository;
public JavaResourcePostController(ResourceRepository repository) {
this.repository = repository;
}
#RequestMapping(value = "/resources", method = RequestMethod.POST)
ResponseEntity<Resource> create(
#Valid #RequestBody Resource resource
) {
repository.save(resource);
return ResponseEntity.ok(resource);
}
}
My understanding is that the resource argument should be valid when entering the method. But posting an empty uri field does not trigger the validation of the method. it does however get triggered in the hibernate validation in repository.save()
Why does the #Valid annotation on the argument not ensure I get a validated entity?
You need to add #Validated to your controller class.
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
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
In Jersey 2 it is possible to do this:
#GET
#PATH("user/{email}")
public IDto getUser(#NotNull #Email #PathParam("email") String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
But I cannot make something similar to work in Spring MVC, it seems that the validation is only done when providing an object in #RequestBody or using an SpringMVC Form, for example the following won't work:
#RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(#NotNull #Email #PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
There are other similar questions, but those seem to be oriented to Spring MVC UI applications, in my case it is only a REST API which returns JSON response so I don't have any View to map/bind to the controller.
Seems it is possible, using #Validated.
Here's an example.
Based on OP's question, this should work:
#RestController
#Validated
public class MyController {
#GetMapping(value="/user/{email}")
public #ResponseBody IDto getUser(#NotNull #Email #PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
}
In plain Spring implementations, it may be required to manually register the validator bean:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
1- Simply add #Validated annotation at the top of your class.
2- Put whatever annotations for validations (#NotBlank, Min(1), etc.) before the #RequestParam annotation in your method signature.
The validated annotation from the org.springframework.validation.annotation.Validated package to validate a #PathVariable. Make sure the class annotated with #Validated.
#GetMapping("/name-for-day/{dayOfWeek}")
public String getNameOfDay(#PathVariable("dayOfWeek") #Min(1) #Max(7) Integer dayOfWeek) {
return dayOfWeek + "";
}
As far as I can tell, you cannot do this out-of-the-box with Spring.
Options:
Use a regular expression:
#RequestMapping(value="/user/{email:SOME_REXEXP}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(#PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
Use Hibernate Validator to validate the method. Either call the validator manually, or make Spring call it for you using AOP. See https://github.com/gunnarmorling/methodvalidation-integration
Controller should be annotated with spring's #Validated
So update your code with
#Validated
#RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(
#NotNull
#Email
#PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
My controller looks like the following:
#RequestMapping(value = "/cars/{types}", method = RequestMethod.PUT,
headers = "Accept=application/json")
#ResponseStatus(HttpStatus.OK)
public void startEngine(
#PathVariable #Min(0) String types, #RequestBody #Valid someObject request, BindingResult result)
throws MethodArgumentNotValidException {
if(result.hasErrors())
{
System.out.println("Error");
//Should I be throwing MethodArgumentNotValidException here? And if so how? I don't know how to retrieve the first parameter for it's constructor (MethodParameter object)
}
//Controller code
}
So after I verify whether or not my result object encountered any errors during validation, how can I then throw the MethodArgumentNotValidException? Or should Spring be already throwing that exception during validation?
If I remember correctly, Spring should throw MethodArgumentNotValidException only if you have not provided an Errors (here, BindingResult) parameter for the #Valid annotated parameter.
You can throw it yourself if you would like to.