Validate a POJO without using #Valid in Spring MVC - java

I need to validate a POJO, without using the annotation #Valid as paramether of a method.
I'm stuck at this point:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Menu>> constraintViolations=validator.validate(menu);
//Menu is my Pojo
if (!constraintViolations.isEmpty()) {
Iterator itr = constraintViolations.iterator();
while(itr.hasNext()){
Object o = itr.next();
}
}
It seems to work, since inside "itr" I have stuff like this::
oConstraintViolationImpl{interpolatedMessage='size must be between 3 and 50', propertyPath=titolo, rootBeanClass=class com.springgestioneerrori.model.Menu, messageTemplate='{javax.validation.constraints.Size.message}'}
Now, my question is: how can I add "itr" values to BindingResult??? Probably I should cast something...somehow...
Thank you

#Test
public void testBindingResult(BindingResult result,Set<ConstraintViolation<String>> violations){
for ( ConstraintViolation<String> constraintViolation : violations) {
ObjectError error=new ObjectError("object",constraintViolation.getMessage());
result.addError(error);
}
}
you should create object of type ObjectError here is a spring doc for the class and you should add it to binding result.

Related

Why does validation not work on objects of type DTO, but only on entities

I have an annotation set over objects of type dto, the same as over objects of type Entity. The annotation works on entities, but it does not work on objects of type dto.
I work in SpringBoot.
application.properties
validate.packageid.size = "The field 'PACKAGEID' can contain only {max} symbols.";
config file
#Configuration
public class ServiceConfig implements WebMvcConfigurer {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setDefaultEncoding("UTF-8");
source.setBasename("classpath:ValidationMessages");
return source;
}
#Nullable
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
}
dto
#Size(message = "{validate.packageid.size}", max = 36)
private String documentId
entity
#Column(name = "DOCUMENTID")
#Size(message = "{validate.packageid.size}", max = 36)
private String documentId;
I cannot use the annotation #Valid because I fill an object of dto type with reflection technology.
public static <S> S fillData (S object, List<Object> values){
return obtainMetadataOfObject(object, values);
}
I need to be able to get the constraint annotation, or rather its parameters, set on the fields of the dto object (but in the case of the dto object I get null, since Spring may not know what to use the constraint annotations set on the fields of the dto object), in the case of entity - it turns out, but the entity validator is engaged by Spritg, since it manages the entity as a component from the Application context.
To validate the dto on the web client side, I use the #Valid annotation in the parameters of the method that handles the request from the client
For validation dto from
Update
I put the annotation #Validation over dto and after that I got the data I need.
It work for classes that don't have classes-heir.
I get data of annotation #Size
private static int getMaxLimitSize(Field field){
field.setAccessible(true);
Size annotation = field.getAnnotation(Size.class);
int zero = 0;
if(annotation == null) return zero;
return annotation.max();
}
But this does not work for objects whose fields are split into several classes : several abstract and one produce.
Validation does not work for composite objects of type DTO, Any help is appreciated?
The validation needs to be triggered somewhere, for entities in your case the spring framework does it (or jpa perhaps). DTOs never make it there. So you have to trigger the validation (validator.validate) on your own, as per the documentation. Here's a related question asking at which application layer to do it.
#PostMapping("/test")
public void test( #RequestBody #Valid final UserDto dto) {
// logic
}
u should add #Valid annotation in controller.
If u want validate dto in service layers u should add #Validate and #Valid :
#Service
#Validated
public class Service {
public void test(#Valid UserDto dto){
// logic
}
}

Hibernate Validator : restrict validation to given constraints

I want to perform the validation of my entities in two steps. While I use a defaultValidatorFactory to validate all the fields of my entities before persisting to the database, I would like to perform a partial validation of my entities at a earlier step. But I cannot find a way to configure my validator (or validatorFactory).
Let's say I have the following class:
public class Car {
#NotNull
private String manufacturer;
#AssertTrue
private boolean isRegistered;
public Car(String manufacturer, boolean isRegistered) {
super();
this.manufacturer = manufacturer;
this.isRegistered = isRegistered;
}
}
When I do the full validation of my entity, I use the given code:
Validator validator = validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Car>> errors = validator.validate(car);
This works fine and validate both annotations NotNull and AssertTrue.
Now, I want to perform an partial validation. I mean by partial validation, I want to only validate, for example, NotNull constraints and ignore other annotations.
Is there a way to get a Validator or ValidatorFactory which uses a custom restricted list of validators?
You can find a lot of things to create your own constraint/constraint validator. In my case, I want to validate only some constraints.
Maybe I can create a custom ConstraintValidatorFactory and inject it in the Validation context? I found that we can reconfigure the context of the factory with the following code, but I don't know how to deal with it.
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validatorFactory.usingContext().constraintValidatorFactory(myCustomFactory);
For the moment, I'm lost. Someone has already done something like that? Do you have any idea how I can do this? Thanks for your time.
I'm using Java 8 and Hibernate Validator 6.0.14.
As Slaw write - use groups.
An Example
package jpatest.jpatest;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.NotNull;
public class TestApp {
/** A validation group marker */
public interface ValidationGroup1 {};
/** The bean */
public static class Bean {
// Validate for group ValidationGroup1
#NotNull(groups = ValidationGroup1.class)
private String s;
}
public static void main(String[] args) {
Bean b = new Bean();
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
// Validation without the validation group => No ConstraintViolation
Set<ConstraintViolation<Bean>> errors1 = validator.validate(b);
assert errors1.isEmpty() : "No ConstraintViolation expected";
// Validation with the validation group => 1 ConstraintViolation
Set<ConstraintViolation<Bean>> errors2 = validator.validate(b, ValidationGroup1.class);
assert errors2.size() == 1 : "1 ConstraintViolation expected";
}
}

Validation of one attribute with Hibernate validator

Good evening, I'm trying to use Hibernate Validator, in the following scenario:
public class Car {
#NotNull
private String manufacturer;
#NotNull
#Size(min = 2, max = 14)
private String licensePlate;
#Min(2)
private int seatCount;
//setters and getters....
}
and I am trying to validate its attributes as follows:
public class CarMain {
public static Validator validator;
public static void main(String[] args) {
ValidatorFactory factory = Validation. buildDefaultValidatorFactory() ;
validator = factory. getValidator();
Car car = new Car(null,null,0);
Set<ConstraintViolation<Car>> st= validator.validate(car);
while(st.iterator.hasNext()){
ConstraintViolation<Car> cv = st.iterator.next();
System.out.println("Value: ("+cv.getInvalidValue()+") -->"+cv.getMessage());
System.out.println("Attribute: "+cv.getPropertyPath());
}
}
Here the whole entity is validated and the invalid values with the validation message and property path are displayed.
My question is:"Is it possible to validate only one attribute at a time with Hibernate Validator? Like I don't have to work with the whole object to validate it.
The Validator interface defines also a [Validator.validateProperty][1] method where you explicitly specify the property to validate. Mind you, you still need the object instance and you need to know the property you want to validate. This method is for example used by the integration of Bean Validation into JSF. Whether it makes sense to use it inm your case, will depend on your use case? Why don't you want to validate the whole object?
BTW, there is also Validator.validateValue which does not require an actual bean instance.

How to implement Jackson custom serialization outside a domain bean?

I have a Spring managed bean...
#Component("Foobean")
#Scope("prototype")
public class foobean {
private String bar1;
private String bar2;
public String getBar1() {
return bar1;
}
public void setBar1(String bar1) {
this.bar1 = bar1;
}
public String getBar2() {
return bar2;
}
public void setBar2(String bar2) {
this.bar2 = bar2;
}
}
...and because I am using Dojo Dgrid to display an ArrayList of this bean, I am returning it into the controller as a JSON string:
#Controller
#RequestMapping("/bo")
public class FooController {
#Autowired
private FooService fooService
#RequestMapping("action=getListOfFoos*")
#ResponseBody
public String clickDisplayFoos(
Map<String, Object> model) {
List<Foobean> foobeans = fooService.getFoobeans();
ObjectMapper objMapper = new ObjectMapper();
String FooJson = null;
try {
FooJson = objMapper.writeValueAsString(foobeans);
} catch (JsonGenerationException e) {
etc.
}
However, my grid needs an additional column which will contain a valid action for each Foo; that action is not really dependent on any data in individual Foos -- they'll all have the same valid action -- repeated on each line of the resulting DGrid -- but that value is actually dependent upon security roles on the session...which can't be sent to the front end in a Json. So, my solution is twofold:
First I need to add a "virtual" Json property to the bean... which I can do in the bean with #JsonProperty on a method...
#JsonProperty("validActions")
public String writeValidActions {
return "placeHolderForSerializerToChange";
}
...but it just generates a placeholder. To really generate a valid value,
I need to reference the security role of the session,
which I am very reluctant to code in the above method. (A service call in
the domain bean itself? Seems very wrong.) I
think I should create a custom serializer and put the logic -- and the reference
to the Session.Security role in there. Are my instincts right, not to
inject session info into a domain bean method? And if so, what would such a
custom serializer look like?
Yes, I wouldn't put Session Info in to the domain or access session directly in my domain.
Unless there is a specific reason, you could simply add the logic in your action class.
public String clickDisplayFoos(){
List<Foo> foos = service.getFoos();
for(iterate through foos){
foo.setValidAction(session.hasSecurityRole())
}
String json = objMapper.writeValueAsString(foobeans);
return json;
}
I don't like the idea of setting new values as part of the serialization process. I feel custom serializers are meant to transform the representation of a particular property rather than add new values to a property.

Manually call Spring Annotation Validation

I'm doing a lot of our validation with Hibernate and Spring Annotations like so:
public class Account {
#NotEmpty(groups = {Step1.class, Step2.class})
private String name;
#NotNull(groups = {Step2.class})
private Long accountNumber;
public interface Step1{}
public interface Step2{}
}
And then in the controller it's called in the arguments:
public String saveAccount(#ModelAttribute #Validated({Account.Step1.class}) Account account, BindingResult result) {
//some more code and stuff here
return "";
}
But I would like to decide the group used based on some logic in the controller method. Is there a way to call validation manually? Something like result = account.validate(Account.Step1.class)?
I am aware of creating your own Validator class, but that's something I want to avoid, I would prefer to just use the annotations on the class variables themselves.
Spring provides LocalValidatorFactoryBean, which implements the Spring SmartValidator interface as well as the Java Bean Validation Validator interface.
// org.springframework.validation.SmartValidator - implemented by LocalValidatorFactoryBean
#Autowired
SmartValidator validator;
public String saveAccount(#ModelAttribute Account account, BindingResult result) {
// ... custom logic
validator.validate(account, result, Account.Step1.class);
if (result.hasErrors()) {
// ... on binding or validation errors
} else {
// ... on no errors
}
return "";
}
Here is a code sample from JSR 303 spec
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Driver driver = new Driver();
driver.setAge(16);
Car porsche = new Car();
driver.setCar(porsche);
Set<ConstraintViolation<Driver>> violations = validator.validate( driver );
So yes, you can just get a validator instance from the validator factory and run the validation yourself, then check to see if there are violations or not. You can see in the javadoc for Validator that it will also accept an array of groups to validate against.
Obviously this uses JSR-303 validation directly instead of going through Spring validation, but I believe spring validation annotations will use JSR-303 if it's found in the classpath
If you have everything correctly configured, you can do this:
import javax.validation.Validator;
#Autowired
Validator validator;
Then you can use it to validate you object:
var errors = validator.validate(obj);
This link gives pretty good examples of using validations in Spring apps.
https://reflectoring.io/bean-validation-with-spring-boot/
I have found an example to run the validation programmitically in this article.
class MyValidatingService {
void validatePerson(Person person) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Person>> violations = validator.validate(person);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
It throws 500 status, so it is recommended to handle it with custom exception handler.
#ControllerAdvice(annotations = RestController.class)
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<CustomErrorResponse> constraintViolationException(HttpServletResponse response, Exception ex) throws IOException {
CustomErrorResponse errorResponse = new CustomErrorResponse();
errorResponse.setTimestamp(LocalDateTime.now());
errorResponse.setStatus(HttpStatus.BAD_REQUEST.value());
errorResponse.setError(HttpStatus.BAD_REQUEST.getReasonPhrase());
errorResponse.setMessage(ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
}
Second example is from https://www.mkyong.com/spring-boot/spring-rest-error-handling-example/
Update:
Using validation is persistence layer is not recommended:
https://twitter.com/odrotbohm/status/1055015506326052865
Adding to answered by #digitaljoel, you can throw the ConstraintViolationException once you got the set of violations.
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<NotionalProviderPaymentDTO>> violations = validator.validate( notionalProviderPaymentDTO );
if(!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
You can create your own exception mapper which will handle ConstraintViolationException and send the errors messages to the client.
And also:
#Autowired
#Qualifier("mvcValidator")
Validator validator;
...
violations = validator.validate(account);
import javax.validation.Validator;
import javax.validation.ConstraintViolation;
public class{
#Autowired
private Validator validator;
.
.
public void validateEmployee(Employee employee){
Set<ConstraintViolation<Employee>> violations = validator.validate(employee);
if(!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
Here, 'Employee' is a pojo class and 'employee' is it's object

Categories