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";
}
}
Related
I'm trying to write a jUnit test for a bean validation.
I read How to test validation annotations of a class using JUnit?
and wrote a test code like as below.
My environment:
Sprint Boot 2.2.6
Java11
AssertJ 3.15.0
Target Bean class:
public class Customer {
#NotEmpty
private String name;
#Min(18)
private int age;
// getter and setter
}
JUnit test code:
public class CustomerValidationTest {
private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
#Test
public void test() {
Customer customer = new Customer(null, 18);
Set<ConstraintViolation<Customer>> violations = validator.validate(customer);
assertThat(violations.size()).isEqualTo(1); // check violations count
// check which constraints are violated by the message of the violation
assertThat(violations).extracting("message").containsOnly("must not be empty");
}
}
I'd like to check which constraints are violated. For now, I check the message of violations.
Is there better way?
In your small test setup you might be able to oversee if exactly and only one violation occurs.
assertThat(violations.size()).isEqualTo(1);
and
.containsOnly("must not be empty")
However in a larger setup that might not be the case. What you actually want to do is asserting your expected violation to be present.
With the Testframework junit-jupiter-api:5.6.2 I did my test like this:
public class CustomerValidationTest {
private static Validator validator;
private static ValidatorFactory factory;
#org.junit.jupiter.api.BeforeEach
void setUp() {
Locale.setDefault(Locale.ENGLISH); //expecting english error messages
factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
#org.junit.jupiter.api.AfterEach
void tearDown() {
factory.close();
}
#org.junit.jupiter.api.Test
public void testContainsEmptyNameViolation() {
Customer customer = new Customer(null, 18);
//perform validation
Set<ConstraintViolation<Customer>> constraintViolations = validator.validate(customer);
boolean hasExpectedPropertyPath = constraintViolations.stream()
.map(ConstraintViolation::getPropertyPath)
.map(Path::toString)
.anyMatch("name"::equals);
boolean hasExpectedViolationMessage = constraintViolations.stream()
.map(ConstraintViolation::getMessage)
.anyMatch("must not be empty"::equals);
assertAll(
() -> assertFalse(constraintViolations.isEmpty()),
() -> assertTrue(hasExpectedPropertyPath),
() -> assertTrue(hasExpectedViolationMessage)
);
Even though you asked for AssertJ, I hope that this might still be of help to you.
This tutorial here shows in section 7. 'Testing .. Validations ..' a nice way of assuming that the expected violation is part of the Set.
Depending on your testing Framework this might be a strategy to follow.
#Test public void validatingObject() {
Car car = new Car();
Set<ConstraintViolation> violations = validator.validate(car);
assertThat(violations.size()).isEqualTo(1);
assertThat(violations)
.anyMatch(havingPropertyPath("customerPropertyPathForCarViolation")
.and(havingMessage("message of desired violation"))); }
I am trying to create a UniqueName annotation as a cutomize bean validation annotation for a create project api:
#PostMapping("/users/{userId}/projects")
public ResponseEntity createNewProject(#PathVariable("userId") String userId,
#RequestBody #Valid ProjectParam projectParam) {
User projectOwner = userRepository.ofId(userId).orElseThrow(ResourceNotFoundException::new);
Project project = new Project(
IdGenerator.nextId(),
userId,
projectParam.getName(),
projectParam.getDescription()
);
...
}
#Getter
#NoArgsConstructor(access = AccessLevel.PRIVATE)
class ProjectParam {
#NotBlank
#NameConstraint
private String name;
private String description;
}
#Constraint(validatedBy = UniqueProjectNameValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD })
public #interface UniqueName {
public String message() default "already existed";
public Class<?>[] groups() default {};
public Class<? extends Payload>[] payload() default{};
}
public class UniqueProjectNameValidator implements ConstraintValidator<UniqueName, String> {
#Autowired
private ProjectQueryMapper mapper;
public void initialize(UniqueName constraint) {
}
public boolean isValid(String value, ConstraintValidatorContext context) {
// how can I get the userId info??
return mapper.findByName(userId, value) == null;
}
}
The problem is that name field just need uniqueness for user level. So I need to get the {userId} from the URL field for validation. But how can I add this into the UniqueProjectNameValidator? Or is there some better way to handle this validation? This is just a small part of a large object, the real object has many other complex validations in the request handler which make the code quite dirty.
As #Abhijeet mentioned, dynamically passing the userId property to the constraint validator is impossible. As to how to handle this validation case better, there's the clean solution and the dirty solution.
The clean solution is to extract all the business logic to a service method, and validate the ProjectParam at the service level. This way, you can add a userId property to ProjectParam, and map it from the #PathVariable onto the #RequestBody before calling the service. You then adjust UniqueProjectNameValidator to validate ProjectParams rather than Strings.
The dirty solution is to use Hibernate Validator's cross-parameter constraints (see also this link for an example). You essentially treat both of your controller method parameters as the input for your custom validator.
If I'm not wrong, what you are asking is, how can you pass your userId to your custom annotation i.e. #UniqueName so that you can access the userId to validate projectName field against existing projectNames for passed userId.
It means you are asking about is, How to pass variable/parameter dynamically to annotation which is not possible. You have to use some other approach like Interceptors or Do the validation manually.
You can refer to the following answers as well:
How to pass value to custom annotation in java?
Passing dynamic parameters to an annotation?
#Mikhail Dyakonov in this article proposed a rule of thumb to choose the best validation method using java:
JPA validation has limited functionality, but it is a great choice for the simplest constraints on entity classes if such
constraints can be mapped to DDL.
Bean Validation is a flexible, concise, declarative, reusable, and readable way to cover most of the checks that you could have in
your domain model classes. This is the best choice, in most cases,
once you don't need to run validations inside a transaction.
Validation by Contract is a Bean validation for method calls. You can use it when you need to check input and output parameters of a
method, for example, in a REST call handler.
Entity listeners although they are not as declarative as the Bean validation annotations, they are a great place to check big
object's graphs or make a check that needs to be done inside a
database transaction. For example, when you need to read some data
from the DB to make a decision, Hibernate has analogs of such
listeners.
Transaction listeners are a dangerous yet ultimate weapon that works inside the transactional context. Use it when you need to decide
at runtime what objects have to be validated or when you need to check
different types of your entities against the same validation
algorithm.
I think Entity listeners match your unique constraint validation issue, because within the Entity Listener you'll be able to access your JPA Entity before persisting/updating it and executing your check query easier.
However as #crizzis pointed me, there is a significant restriction with this approach. As stated in JPA 2 specification (JSR 317):
In general, the lifecycle method of a portable application should not
invoke EntityManager or Query operations, access other entity
instances, or modify relationships within the same persistence
context. A lifecycle callback method may modify the non-relationship
state of the entity on which it is invoked.
Whether you try this approach, first you'll need an ApplicationContextAware implementation for getting current EntityManager instance. It's an old Spring Framework trick, maybe You're already using it.
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public final class BeanUtil implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CONTEXT = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return CONTEXT.getBean(beanClass);
}
}
This is my Entity Listener
#Slf4j
public class GatewaUniqueIpv4sListener {
#PrePersist
void onPrePersist(Gateway gateway) {
try {
EntityManager entityManager = BeanUtil.getBean(EntityManager.class);
Gateway entity = entityManager
.createQuery("SELECT g FROM Gateway g WHERE g.ipv4 = :ipv4", Gateway.class)
.setParameter("ipv4", gateway.getIpv4())
.getSingleResult();
// Already exists a Gateway with the same Ipv4 in the Database or the PersistenceContext
throw new IllegalArgumentException("Can't be to gateways with the same Ip address " + gateway.getIpv4());
} catch (NoResultException ex) {
log.debug(ex.getMessage(), ex);
}
}
}
Finally, I added this annotation to my Entity Class #EntityListeners(GatewaUniqueIpv4sListener.class)
You can find the complete working code here gateways-java
A clean and simple approach could be check validations in which you need to access the database within your transactional services. Even you could use the Specification, Strategy, and Chain of Responsibility patterns in order to implement a better solution.
I believe you can do what you're asking, but you might need to generalize your approach just a bit.
As others have mentioned, you can not pass two attributes into a validator, but, if you changed your validator to be class level validator instead of a field level validator, it can work.
Here is a validator we created that makes sure that two fields are the same value when submitted. Think of the password and confirm password use case that you often see websites, or email and confirm email use case.
Of course, in your particular case, you'll need to pass in the user's id and the name of the project that they are trying to create.
Annotation:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Taken from:
* http://stackoverflow.com/questions/1972933/cross-field-validation-with-hibernate-validator-jsr-303
* <p/>
* Validation annotation to validate that 2 fields have the same value.
* An array of fields and their matching confirmation fields can be supplied.
* <p/>
* Example, compare 1 pair of fields:
*
* #FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match")
* <p/>
* Example, compare more than 1 pair of fields:
* #FieldMatch.List({
* #FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match"),
* #FieldMatch(first = "email", second = "confirmEmail", message = "The email fields must match")})
*/
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = FieldMatchValidator.class)
#Documented
public #interface FieldMatch {
String message() default "{constraints.fieldmatch}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* #return The first field
*/
String first();
/**
* #return The second field
*/
String second();
/**
* Defines several <code>#FieldMatch</code> annotations on the same element
*
* #see FieldMatch
*/
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Documented
#interface List {
FieldMatch[] value();
}
}
The Validator:
import org.apache.commons.beanutils.BeanUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* Taken from:
* http://stackoverflow.com/questions/1972933/cross-field-validation-with-hibernate-validator-jsr-303
*/
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
private String firstFieldName;
private String secondFieldName;
#Override
public void initialize(FieldMatch constraintAnnotation) {
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
try {
Object firstObj = BeanUtils.getProperty(value, firstFieldName);
Object secondObj = BeanUtils.getProperty(value, secondFieldName);
return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
} catch (Exception ignore) {
// ignore
}
return true;
}
}
Then here our command object:
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.GroupSequence;
#GroupSequence({Required.class, Type.class, Data.class, Persistence.class, ChangePasswordCommand.class})
#FieldMatch(groups = Data.class, first = "password", second = "confirmNewPassword", message = "The New Password and Confirm New Password fields must match.")
public class ChangePasswordCommand {
#NotBlank(groups = Required.class, message = "New Password is required.")
#Length(groups = Data.class, min = 6, message = "New Password must be at least 6 characters in length.")
private String password;
#NotBlank(groups = Required.class, message = "Confirm New Password is required.")
private String confirmNewPassword;
...
}
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.
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
I would like to know what is the cleanest and best way to perform form validation of user inputs. I have seen some developers implement org.springframework.validation.Validator. A question about that: I saw it validates a class. Does the class have to be filled manually with the values from the user input, and then passed to the validator?
I am confused about the cleanest and best way to validate the user input. I know about the traditional method of using request.getParameter() and then manually checking for nulls, but I don't want to do all the validation in my Controller. Some good advice on this area will be greatly appreciated. I am not using Hibernate in this application.
With Spring MVC, there are 3 different ways to perform validation : using annotation, manually, or a mix of both. There is not a unique "cleanest and best way" to validate, but there is probably one that fits your project/problem/context better.
Let's have a User :
public class User {
private String name;
...
}
Method 1 : If you have Spring 3.x+ and simple validation to do, use javax.validation.constraints annotations (also known as JSR-303 annotations).
public class User {
#NotNull
private String name;
...
}
You will need a JSR-303 provider in your libraries, like Hibernate Validator who is the reference implementation (this library has nothing to do with databases and relational mapping, it just does validation :-).
Then in your controller you would have something like :
#RequestMapping(value="/user", method=RequestMethod.POST)
public createUser(Model model, #Valid #ModelAttribute("user") User user, BindingResult result){
if (result.hasErrors()){
// do something
}
else {
// do something else
}
}
Notice the #Valid : if the user happens to have a null name, result.hasErrors() will be true.
Method 2 : If you have complex validation (like big business validation logic, conditional validation across multiple fields, etc.), or for some reason you cannot use method 1, use manual validation. It is a good practice to separate the controller’s code from the validation logic. Don't create your validation class(es) from scratch, Spring provides a handy org.springframework.validation.Validator interface (since Spring 2).
So let's say you have
public class User {
private String name;
private Integer birthYear;
private User responsibleUser;
...
}
and you want to do some "complex" validation like : if the user's age is under 18, responsibleUser must not be null and responsibleUser's age must be over 21.
You will do something like this
public class UserValidator implements Validator {
#Override
public boolean supports(Class clazz) {
return User.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
User user = (User) target;
if(user.getName() == null) {
errors.rejectValue("name", "your_error_code");
}
// do "complex" validation here
}
}
Then in your controller you would have :
#RequestMapping(value="/user", method=RequestMethod.POST)
public createUser(Model model, #ModelAttribute("user") User user, BindingResult result){
UserValidator userValidator = new UserValidator();
userValidator.validate(user, result);
if (result.hasErrors()){
// do something
}
else {
// do something else
}
}
If there are validation errors, result.hasErrors() will be true.
Note : You can also set the validator in a #InitBinder method of the controller, with "binder.setValidator(...)" (in which case a mix use of method 1 and 2 would not be possible, because you replace the default validator). Or you could instantiate it in the default constructor of the controller. Or have a #Component/#Service UserValidator that you inject (#Autowired) in your controller : very useful, because most validators are singletons + unit test mocking becomes easier + your validator could call other Spring components.
Method 3 :
Why not using a combination of both methods? Validate the simple stuff, like the "name" attribute, with annotations (it is quick to do, concise and more readable). Keep the heavy validations for validators (when it would take hours to code custom complex validation annotations, or just when it is not possible to use annotations). I did this on a former project, it worked like a charm, quick & easy.
Warning : you must not mistake validation handling for exception handling. Read this post to know when to use them.
References :
A very interesting blog post about bean validation (Original link is dead)
Another good blog post about validation (Original link is dead)
Latest Spring documentation about validation
There are two ways to validate user input: annotations and by inheriting Spring's Validator class. For simple cases, the annotations are nice. If you need complex validations (like cross-field validation, eg. "verify email address" field), or if your model is validated in multiple places in your application with different rules, or if you don't have the ability to modify your model object by placing annotations on it, Spring's inheritance-based Validator is the way to go. I'll show examples of both.
The actual validation part is the same regardless of which type of validation you're using:
RequestMapping(value="fooPage", method = RequestMethod.POST)
public String processSubmit(#Valid #ModelAttribute("foo") Foo foo, BindingResult result, ModelMap m) {
if(result.hasErrors()) {
return "fooPage";
}
...
return "successPage";
}
If you are using annotations, your Foo class might look like:
public class Foo {
#NotNull
#Size(min = 1, max = 20)
private String name;
#NotNull
#Min(1)
#Max(110)
private Integer age;
// getters, setters
}
Annotations above are javax.validation.constraints annotations. You can also use Hibernate's
org.hibernate.validator.constraints, but it doesn't look like you are using Hibernate.
Alternatively, if you implement Spring's Validator, you would create a class as follows:
public class FooValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Foo.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Foo foo = (Foo) target;
if(foo.getName() == null) {
errors.rejectValue("name", "name[emptyMessage]");
}
else if(foo.getName().length() < 1 || foo.getName().length() > 20){
errors.rejectValue("name", "name[invalidLength]");
}
if(foo.getAge() == null) {
errors.rejectValue("age", "age[emptyMessage]");
}
else if(foo.getAge() < 1 || foo.getAge() > 110){
errors.rejectValue("age", "age[invalidAge]");
}
}
}
If using the above validator, you also have to bind the validator to the Spring controller (not necessary if using annotations):
#InitBinder("foo")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new FooValidator());
}
Also see Spring docs.
Hope that helps.
I would like to extend nice answer of Jerome Dalbert. I found very easy to write your own annotation validators in JSR-303 way. You are not limited to have "one field" validation. You can create your own annotation on type level and have complex validation (see examples below). I prefer this way because I don't need mix different types of validation (Spring and JSR-303) like Jerome do. Also this validators are "Spring aware" so you can use #Inject/#Autowire out of box.
Example of custom object validation:
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = { YourCustomObjectValidator.class })
public #interface YourCustomObjectValid {
String message() default "{YourCustomObjectValid.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class YourCustomObjectValidator implements ConstraintValidator<YourCustomObjectValid, YourCustomObject> {
#Override
public void initialize(YourCustomObjectValid constraintAnnotation) { }
#Override
public boolean isValid(YourCustomObject value, ConstraintValidatorContext context) {
// Validate your complex logic
// Mark field with error
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(someField).addConstraintViolation();
return true;
}
}
#YourCustomObjectValid
public YourCustomObject {
}
Example of generic fields equality:
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = { FieldsEqualityValidator.class })
public #interface FieldsEquality {
String message() default "{FieldsEquality.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Name of the first field that will be compared.
*
* #return name
*/
String firstFieldName();
/**
* Name of the second field that will be compared.
*
* #return name
*/
String secondFieldName();
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
public #interface List {
FieldsEquality[] value();
}
}
import java.lang.reflect.Field;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
public class FieldsEqualityValidator implements ConstraintValidator<FieldsEquality, Object> {
private static final Logger log = LoggerFactory.getLogger(FieldsEqualityValidator.class);
private String firstFieldName;
private String secondFieldName;
#Override
public void initialize(FieldsEquality constraintAnnotation) {
firstFieldName = constraintAnnotation.firstFieldName();
secondFieldName = constraintAnnotation.secondFieldName();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null)
return true;
try {
Class<?> clazz = value.getClass();
Field firstField = ReflectionUtils.findField(clazz, firstFieldName);
firstField.setAccessible(true);
Object first = firstField.get(value);
Field secondField = ReflectionUtils.findField(clazz, secondFieldName);
secondField.setAccessible(true);
Object second = secondField.get(value);
if (first != null && second != null && !first.equals(second)) {
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(firstFieldName).addConstraintViolation();
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(someField).addConstraintViolation(secondFieldName);
return false;
}
} catch (Exception e) {
log.error("Cannot validate fileds equality in '" + value + "'!", e);
return false;
}
return true;
}
}
#FieldsEquality(firstFieldName = "password", secondFieldName = "confirmPassword")
public class NewUserForm {
private String password;
private String confirmPassword;
}
If you have same error handling logic for different method handlers, then you would end up with lots of handlers with following code pattern:
if (validation.hasErrors()) {
// do error handling
}
else {
// do the actual business logic
}
Suppose you're creating RESTful services and want to return 400 Bad Request along with error messages for every validation error case. Then, the error handling part would be same for every single REST endpoint that requires validation. Repeating that very same logic in every single handler is not so DRYish!
One way to solve this problem is to drop the immediate BindingResult after each To-Be-Validated bean. Now, your handler would be like this:
#RequestMapping(...)
public Something doStuff(#Valid Somebean bean) {
// do the actual business logic
// Just the else part!
}
This way, if the bound bean was not valid, a MethodArgumentNotValidException will be thrown by Spring. You can define a ControllerAdvice that handles this exception with that same error handling logic:
#ControllerAdvice
public class ErrorHandlingControllerAdvice {
#ExceptionHandler(MethodArgumentNotValidException.class)
public SomeErrorBean handleValidationError(MethodArgumentNotValidException ex) {
// do error handling
// Just the if part!
}
}
You still can examine the underlying BindingResult using getBindingResult method of MethodArgumentNotValidException.
Find complete example of Spring Mvc Validation
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.technicalkeeda.bean.Login;
public class LoginValidator implements Validator {
public boolean supports(Class aClass) {
return Login.class.equals(aClass);
}
public void validate(Object obj, Errors errors) {
Login login = (Login) obj;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName",
"username.required", "Required field");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userPassword",
"userpassword.required", "Required field");
}
}
public class LoginController extends SimpleFormController {
private LoginService loginService;
public LoginController() {
setCommandClass(Login.class);
setCommandName("login");
}
public void setLoginService(LoginService loginService) {
this.loginService = loginService;
}
#Override
protected ModelAndView onSubmit(Object command) throws Exception {
Login login = (Login) command;
loginService.add(login);
return new ModelAndView("loginsucess", "login", login);
}
}
Put this bean in your configuration class.
#Bean
public Validator localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
and then You can use
<T> BindingResult validate(T t) {
DataBinder binder = new DataBinder(t);
binder.setValidator(validator);
binder.validate();
return binder.getBindingResult();
}
for validating a bean manually. Then You will get all result in BindingResult and you can retrieve from there.
Validation groups
Also it is worth to mention validation for some more complex cases, when you have some "multi steps" within your business logic. In such cases we need "validation groups".
#Validated annotation was added to support "validation groups" in validated bean. This can be used in multi step forms where in the first step you need, for example, validate name and email, and in the second step you need to validate, for example, phone number.
With #Validated you first need to declare groups. Groups are declared with your custom marker interfaces.
#Validated example
Let's say we have a scenario when we have a form for user sign up. On this form we want user to provide a name and email. And after user is signed up we have another form where we suggest the user to add his some extra information, for example, email. We don't want email be provided on the first step. But it is required to provide it on the second step.
For this case, we'll declare two groups. First group would be OnCreate, and the second group would be OnUpdate :
OnCreate:
public interface OnCreate {}
OnUpdate:
public interface OnUpdate {}
Our user UserAccount class:
public class UserAccount {
// we will return this field after User is created
// and we want this field to be provided only on update
// so we can determine which user needs to be updated
#NotBlank(groups = OnUpdate.class)
private String id;
#NotBlank(groups = OnCreate.class)
private String name;
#NotBlank(groups = OnCreate.class)
private String email;
#NotBlank(groups = OnUpdate.class)
private String phone;
// standard constructors / setters / getters / toString
}
We mark the validation annotations with our groups interfaces depending on which group those validations are supposed to be related.
And finally our Controller methods:
#PostMapping(value = "/create")
public UserAccount createAccount(#Validated(OnCreate.class) #RequestBody UserAccount userAccount) {
...
}
#PatchMapping(value = "/update")
public UserAccount updateAccount(#Validated(OnUpdate.class) #RequestBody UserAccount userAccount) {
...
}
Here we specify #Validated(...) instead of #Valid and specify the validation group which should be used in different cases.
Now depending on validation group we'll perform the validations for the particular fields within different steps.