How to use #valid annotation in complex model - java

I have complex model request class and I am using valid annotation. But it doesnt work in subclasses.
cause=java.lang.NullPointerException detailMessage=HV000028:
Unexpected exception during isValid call.
public class ChangeBlackListStatusRequest {
#Valid
List<CategoryChangeRequest> categoryChangeRequestList;
}
public class CategoryChangeRequest {
#Valid
List<Category> categoryList;
#Valid
List<Service> serviceList;
#Valid
List<Merchant> merchantList;
#Valid
List<Aggregator> aggregatorList;
}

Related

springboot #validated and bindingresult

#Controller
#RequestMapping("/")
#Validated
public class AddressWebController {
#GetMapping(value = {"/{id}")
public String editForm(#PathVariable(value = "id") #Positive Long id, Model model) {
// process
}
#PutMapping(value = {"/edit"})
public String editProcess(#ModelAttribute #Valid Form form,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
// ???
}
// process
}
}
public class Form {
#NotBlank
private String name;
}
curl -XGET 'http://localhost/5'
is pass
curl -XGET 'http://localhost/-5'
A ConstraintViolationException exception is thrown. Due to #Validated and #Positive
curl 'http://localhost/edit' --data-raw '_method=PUT&name='
I expected it to come in as bindingResult.hasErrors() in that part, but the same ConstraintViolationException is thrown.
When #Validated is removed, bindingResult.hasErrors() works, but #Positive annotation does not.
How can I use #Validated annotation and BindingResult together?

Spring boot and Bean validation in different methods and the same class

I'm doing a rest webservice with spring boot and I would like to know if is possible do different validations with bean validation annotations by method with a POJO as param in the controller layer.
example:
POJO:
Public class Person{
#NotNull(forMethod="methodOne")
private String firstName;
#NotNull(forMehotd="methodTwo")
private String lastName;
private String age;
//getter and setter
}
Controller
#RestController
public class controller{
#RequestMapping(....)
public ResponseEntity methodOne(#Valid #RequestBody Person person){
.....
}
#RequestMapping(....)
public ResponseEntity methodTwo(#Valid #RequestBody Person person){
......
}
}
I know that is possible do it with separate parameters in the methods, but I have a POJO with so many attributes. is it possible do something like that?
I think you should use validation groups in your bean validation annotations and use #Validated annotation instead of #Valid annotation. because #Validated annotation has a value properties that specifies a group for validation.
for example:
Public class Person{
#NotNull(groups={MethodOne.class})
private String firstName;
#NotNull(groups={MethodTwo.class})
private String lastName;
private String age;
//getter and setter
}
and
#RestController
public class controller{
#RequestMapping(....)
public ResponseEntity methodOne(#Validated(MethodOne.class) #RequestBody Person person){
.....
}
#RequestMapping(....)
public ResponseEntity methodTwo(#Validated(MethodTwo.class) #RequestBody Person person){
......
}
}
by the way, don't forget that you should create MethodOne and MethodTwo interfaces to use them as your validation groups.

Spring boot REST controller: different custom validators for POST and PUT methods receiving the same object

I have a Spring Boot Controller with POST and PUT method's and a custom validator.
#Autowired
PersonValidator validator;
#InitBinder
protected void initBinder(final WebDataBinder binder) {
binder.addValidators(validator);
}
#PostMapping
public ResponseEntity<String> save(#Valid #RequestBody Person person) {}
#PutMapping
public ResponseEntity<String> update(#Valid #RequestBody Person person) {}
Currently both POST and PUT methods are using the same validation rules. QUESTION: I would need to have different validation rules for PUT and POST. Any ideas on how to proceed, how can I use different custom validators within the same RestController?
You can integrate Hibernate Validator in your application if you are using Spring boot. It seems that you need to implement Grouping constraints (https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#chapter-groups)
e.g.
Entity
#NotEmpty(groups = PutValidation.class)
private String field1;
#Size(min = 3, groups = PutValidation.class)
#Size(min = 5, groups = PostValidation.class)
private String field2;
#Min(value = 18, groups = {PostValidation.class,PutValidation.class,Default.class})
private int age;
//Getters/setters
Groups (these are nothing but empty interfaces)
public interface PostValidation {
}
public interface PutValidation {
}
How to use group of constraints in Controller
#PostMapping
public ResponseEntity<String> save(#Validated(PostValidation.class) #RequestBody Person person) {}
#PutMapping
public ResponseEntity<String> update(#Validated(PutValidation.class) #RequestBody Person person) {}
I hope this can help to solve your problem.
Cheers!
You could create your own annotations for each Validator and replace #Valid at each endpoint.
Have a look at #InRange: https://lmonkiewicz.com/programming/get-noticed-2017/spring-boot-rest-request-validation/

Spring #RestController with #RequestBody #Valid by default

Usually we write
#RestController
public class TestController {
#RequestMapping(value = "/test")
public String test2(#RequestBody #Valid TestClass req) {
return "test2";
}
}
But since it is a REST controller is it possible to configure Spring to use #RequestBody #Valid by default, so these annotations could be omitted?

Spring boot, how to use #Valid with List<T>

I am trying to put validation to a Spring Boot project. So I put #NotNull annotation to Entity fields. In controller I check it like this:
#RequestMapping(value="", method = RequestMethod.POST)
public DataResponse add(#RequestBody #Valid Status status, BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
return new DataResponse(false, bindingResult.toString());
}
statusService.add(status);
return new DataResponse(true, "");
}
This works. But when I make it with input List<Status> statuses, it doesn't work.
#RequestMapping(value="/bulk", method = RequestMethod.POST)
public List<DataResponse> bulkAdd(#RequestBody #Valid List<Status> statuses, BindingResult bindingResult) {
// some code here
}
Basically, what I want is to apply validation check like in the add method to each Status object in the requestbody list. So, the sender will now which objects have fault and which has not.
How can I do this in a simple, fast way?
My immediate suggestion is to wrap the List in another POJO bean. And use that as the request body parameter.
In your example.
#RequestMapping(value="/bulk", method = RequestMethod.POST)
public List<DataResponse> bulkAdd(#RequestBody #Valid StatusList statusList, BindingResult bindingResult) {
// some code here
}
and StatusList.java will be
#Valid
private List<Status> statuses;
//Getter //Setter //Constructors
I did not try it though.
Update:
The accepted answer in this SO link gives a good explanation why bean validation are not supported on Lists.
Just mark controller with #Validated annotation.
It will throw ConstraintViolationException, so probably you will want to map it to 400: BAD_REQUEST:
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
#ControllerAdvice(annotations = Validated.class)
public class ValidatedExceptionHandler {
#ExceptionHandler
public ResponseEntity<Object> handle(ConstraintViolationException exception) {
List<String> errors = exception.getConstraintViolations()
.stream()
.map(this::toString)
.collect(Collectors.toList());
return new ResponseEntity<>(new ErrorResponseBody(exception.getLocalizedMessage(), errors),
HttpStatus.BAD_REQUEST);
}
private String toString(ConstraintViolation<?> violation) {
return Formatter.format("{} {}: {}",
violation.getRootBeanClass().getName(),
violation.getPropertyPath(),
violation.getMessage());
}
public static class ErrorResponseBody {
private String message;
private List<String> errors;
}
}
#RestController
#Validated
#RequestMapping("/products")
public class ProductController {
#PostMapping
#Validated(MyGroup.class)
public ResponseEntity<List<Product>> createProducts(
#RequestBody List<#Valid Product> products
) throws Exception {
....
}
}
with using Kotlin and Spring Boot Validator
#RestController
#Validated
class ProductController {
#PostMapping("/bulk")
fun bulkAdd(
#Valid
#RequestBody statuses: List<Status>,
): ResponseEntity<DataResponse>> {...}
}
data class Status(
#field:NotNull
val status: String
)

Categories