Spring MVC form validation - spring

my problem is that I have a form which has html select element with some choosing option value & I want to validate those value using :
org.hibernate.validator.constraints
or
javax.validation.constraints
annotations. here is my select element:
<select name="status" id="tbSelect">
<option value="ACTIVE">ACTIVE</option>
<option value="LISTEN">LISTEN</option>
<option value="DOWN">DOWN</option>
</select>
how I can for example validate the value of the options(DOWN,LISTEN,ACTIVE) inside the select element by using the annotation validators which I mention above?
my form is like this :
<form:form action="../agents/add" method="POST" commandName="myAgent">
<form:select id="tbSelect" path="state">
<form:option value="ACTIVE" path="state">ACTIVE</form:option>
<form:option value="LISTEN" path="state">LISTEN</form:option>
<form:option value="DOWN" path="state">DOWN</form:option>
</form:select>
I have defined my controller method like this:
#RequestMapping(value = "agents/add", method = RequestMethod.POST)
public String addAgentSubmit(#ModelAttribute("myAgent") #Valid final AgentValidator agent, BindingResult result, RedirectAttributes redirect) {
if (result.hasErrors()) {
return "admin/agent/add";
}
...
}
and I also define a ModelAttribute like this:
#ModelAttribute("myAgent")
public AgentValidator getLoginForm() {
return new AgentValidator();
}
Here is my AgentValidator class also:
public class AgentValidator {
#NotEmpty(message = "your state can not be empty !")
private String state;

Since your state field looks more like an enumeration, first of all I would recommend to change state field into enum, let Spring MVC to bind that field and use only #NotNull annotation:
public class AgentValidator {
#NotNull(message = "your state can not be empty !")
private AgenState state;
Where AgentState is:
public enum AgentState {
DOWN,LISTEN,ACTIVE
}
But if for certain reasons you can't change your model, then you may use custom constraints.
Particular you need to create your annotation AgentStateConstraint:
#Target( { METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = AgentStateConstraintValidator.class)
#Documented
public #interface AgentStateConstraint {
String message() default "Some message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Then you need to create validator AgentStateConstraintValidator:
public class AgentStateConstraintValidator implements ConstraintValidator<AgentStateConstraint, String> {
//Accepted values
private static final Set<String> ACCEPTED_VALUES = new HashSet<String>(
Arrays.asList(
"DOWN",
"LISTEN",
"ACTIVE"
)
);
public void initialize(AgentStateConstraint constraintAnnotation) {
}
public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
return ACCEPTED_VALUES.contains(object);
}
}

Related

Springboot #Valid annotation with String object

So, I have the following controller method:
#RequestMapping(path = "/{application}/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public MyObject getUsers(#RequestParam("itemId") String itemId, #PathVariable("application") String application) {
return userService.get(itemId, application);
}
I would like to check if the request parameter itemId exists in the related application (in the path).
My first idea was to create a validator :
#RequestMapping(path = "/{application}/users", method = RequestMethod.GET, produces =
MediaType.APPLICATION_JSON_VALUE)
#CheckItemId
public MyObject getUsers(#RequestParam("itemId") String itemId, #PathVariable("application") String application) {
return userService.get(itemId, application);
}
CheckItemId.java :
#Target({METHOD})
#Retention(RUNTIME)
#Constraint(validatedBy = CheckItemIdValidator.class)
#Documented
public #interface CheckItemId {
String message() default "error";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
CheckItemIdValidator.java :
#SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class CheckItemIdValidator implements ConstraintValidator<CheckItemId, Object[]>{
#Override
public boolean isValid(Object[] arg0, ConstraintValidatorContext arg1) {
String itemId= (String) arg0[0];
String application = (String) arg0[1];
// Logic business ...
return true;
}
}
This implementation works well, I managed to get the values itemId and application in the validator. I can now do my verification.
I was wondering if there is a better way to do something like that? Since I handle an array of Object, I need to cast it to String and If I change the parameters order, I will not get the same values since I need to use arg0[0] and arg0[1].
Thank you !
You can use spring validation library. Add #Valid on controller level. Then add #NotBlank on method level as below.
getUsers(#RequestParam("itemId") #NotBlank String itemId)

Is there an annotation for java validate if the value of a field in a List of Objects is duplicated?

I have a List of Objects and each Object have an email, I'd like to validate if the email is duplicated in this list.
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
Father create(#PathVariable String id,
#Valid #RequestBody Father father) {
...
}
Father will have a list of child:
private List<Child> childs;
Each child will have an email:
public class Child {
...
#NotEmpty
private String email;
...
}
I'd like to validate if for example there is a request body with 2 child with the same email.
Is it possible or only validating after receive and process the payload?
Edited
For validating the child emails list, you can create a custom validation.
I coded a custom validation as follows
1- Create annotation named ChildEmailValidation
#Documented
#Constraint(validatedBy = ChildEmailValidator.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface ChildEmailValidation {
String message() default "Duplicate Email";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2- Create a validator for ChildEmailValidation
In this part, you can write your custom business for validation. (You can write your algorithm)
public class ChildEmailValidator implements ConstraintValidator<ChildEmailValidation, List<Child>> {
#Override
public void initialize(ChildEmailValidation constraintAnnotation) {
}
#Override
public boolean isValid(List<Child> childList, ConstraintValidatorContext constraintValidatorContext) {
//Create empty mailList
List<String> mailList = new ArrayList<>();
//Iterate on childList
childList.forEach(child -> {
//Checks if the mailList has the child's email
if (mailList.contains(child.getMail())) {
//Found Duplicate email
throw new DuplicateEmailException();
}
//Add the child's email to mailList (If duplicate email is not found)
mailList.add(child.getMail());
});
//There is no duplicate email
return true;
}
}
3- Add #ChildEmailValidation in Father class
public class Father {
List<Child> child;
#ChildEmailValidation
public List<Child> getChild() {
return child;
}
public void setChild(List<Child> child) {
this.child = child;
}
}
4- Put #Valid on fatherDto in the controller
#RestController
#RequestMapping(value = "/test/", method = RequestMethod.POST)
public class TestController {
#RequestMapping(value = "/test")
public GenericResponse getFamily(#RequestBody #Valid Father fatherDto) {
// ...
}
}
You can use #UniqueElements annotation of Hibernate that you can find on this documentation this is based on equals of some element.
#UniqueElements
private List<Child> childs;

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction

I have a error when I try to insert a user in the database.
I made i custom annotation which verify if the password match with the confirmation password it works when the field not matches , but when the passowrd matches i have this error :
This is my code This is my field match #Annotation :
package mereuta.marian.tennis01.annotations;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Constraint(validatedBy = FieldsValueMatchValidator.class)
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface FieldsValueMatch {
String message() default "Fields values don't match!";
String field();
String fieldMatch();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#interface List {
FieldsValueMatch[] value();
}
}
This is the Field Validator :
package mereuta.marian.tennis01.annotations;
import mereuta.marian.tennis01.model.Utilisateur;
import org.springframework.beans.BeanWrapperImpl;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class FieldsValueMatchValidator implements ConstraintValidator<FieldsValueMatch , Object> {
private String field;
private String fieldMatch;
#Override
public void initialize(FieldsValueMatch fieldsValueMatch) {
this.field=fieldsValueMatch.field();
this.fieldMatch=fieldsValueMatch.fieldMatch();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
Object fieldValue = new BeanWrapperImpl(value)
.getPropertyValue(field);
Object fieldMatchValue = new BeanWrapperImpl(value)
.getPropertyValue(fieldMatch);
if (fieldValue != null) {
return fieldValue.equals(fieldMatchValue);
} else {
return fieldMatchValue == null;
}
}
}
This is my Entity :
#FieldsValueMatch(field = "password", fieldMatch = "confirmPassword",
message = "Password do not match!")
#Entity(name = "utilisateurs")
public class Utilisateur {
#Id #GeneratedValue
#Column(name = "id_utilisateur")
private Integer id;
#NotNull
#Size(min = 4, max = 255)
#Column(name = "password")
private String password;
#Transient
#NotNull
private String confirmPassword;
This is the Controller :
#PostMapping("/addUtilisateur")
public String addUtilisateur(#Valid #ModelAttribute("utilisateur") Utilisateur utilisateur, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors() ) {
model.addAttribute("message", "le mot de passe ne correspond pas");
return "utilisateur/formRegister";
}
utilisateurMetier.creerUtilisateur(utilisateur);
return "utilisateur/utilisateurAjoute";
}
And finally the View :
<div class="container">
<form id="contact" th:action="#{addUtilisateur}" method="post" th:object="${utilisateur}">
<h3>Créer compte</h3>
<input placeholder="password" type="password" th:field="*{password}" tabindex="2" required/>
<span class="text text-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span>
</fieldset>
<fieldset>
<input placeholder="password" type="password" th:field="*{confirmPassword}" tabindex="2" required/>
<span class="text text-danger" th:if="${#fields.hasErrors('confirmPassword')}"
th:errors="*{confirmPassword}" th:text="${message}"></span>
</fieldset>
For the custom annotations I find a example on : https://www.baeldung.com/spring-mvc-custom-validator
#Override
public void creerUtilisateur(Utilisateur utilisateur) {
Role role;
float credit = 0;
boolean actif = true;
role = roleRepository.getOne(3);
System.out.println(role);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
utilisateur.setPassword(encoder.encode(utilisateur.getPassword()));
utilisateur.setRole(role);
utilisateur.setCredit(credit);
utilisateur.setActif(actif);
utilisateurRepository.save(utilisateur);
}
Thank you in advance for your help
As already mentioned the ContraintViolationException is thrown inside the 'creerUtilisateur' method. So the validation of your Utilisateur bean at the time it's passed to your Spring MVC controller method (addUtilisateur(#Valid #ModelAttribute("utilisateur")...) works correctly when both fields (password, confirmPassword) have the same value. Later, you encode the password and change the value of your Utilitsateur's 'password' instance variable:
utilisateur.setPassword(encoder.encode(utilisateur.getPassword()));
Now, 'password' and 'passwordConfirm' are not equal anymore! When persisting this entity in utilisateurRepository.save(utilisateur); JPA will again bean-validate your entity before saving it to database (pre-persist). The validation gets automatically executed when JPA/Hibernate triggers a pre-persist, pre-update or pre-remove lifecycle event. And then the ContraintViolationException is thrown!
In your creerUtilisateur method simply set the encoded password for both, 'password' and 'passwordConfirm', instance variables and hereby ensure that they still pass your equality check in FieldsValueMatchValidator.isValid(Object value, ConstraintValidatorContext context):
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
final String encodedPassword = encoder.encode(utilisateur.getPassword());
utilisateur.setPassword(encodedPassword);
utilisateur.setPasswordConfirm(encodedPassword);
//...
utilisateurRepository.save(utilisateur);
You could also try to customize JPA's bean validation behaviour:
https://www.thoughts-on-java.org/automatically-validate-entities-with-hibernate-validator/
Disable Hibernate validation for Merge/Update

Spring class level validation and Thymeleaf

I am learning Spring Framework and Thymeleaf. I have known how to display field error by using something like ${#fields.errors("xx")}. However, I get stuck about how to display object error message in Thymeleaf.
Here is my UserForm class:
#PasswordMatches
public class UserForm {
#NotNull
#NotEmpty
private String username;
#NotNull
#NotEmpty
private String password;
#NotNull
#NotEmpty
private String matchingPassword;
#NotNull
#NotEmpty
#ValidEmail
private String email;
/* setter and getter methods */
Here is my PasswordMatches annotation:
#Target({ElementType.TYPE, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = PasswordMatchesValidator.class)
#Documented
public #interface PasswordMatches {
String message() default "Passwords don't match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {
#Override
public void initialize(PasswordMatches constraintAnnotation) {
}
#Override
public boolean isValid(Object obj, ConstraintValidatorContext context){
UserDto user = (UserDto) obj;
return user.getPassword().equals(user.getMatchingPassword());
}
}
Here is my Controller method:
#RequestMapping(value="/registration", method=RequestMethod.POST)
public ModelAndView registerUserAccount(#ModelAttribute("user") #Valid UserForm userForm,
BindingResult result, WebRequest request, Errors errors) {
if (!result.hasErrors()) {
return new ModelAndView("registerSuccess");
}
else {
return new ModelAndView("registration", "user", userForm);
}
}
Now here is my problem: If the password field and confirmPass field doesn't match, how can I get the default error message returned by the class level annotation in Thymeleaf?
I know this is old post but I also encountered this problem and here is the soulution (maybe it will also help someone else):
Modify PasswordMatchesValidator to this:
class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {
#Override
public void initialize(PasswordMatches constraintAnnotation) {
}
#Override
public boolean isValid(Object obj, ConstraintValidatorContext context){
UserDto user = (UserDto) obj;
boolean isValid = user.getPassword().equals(user.getMatchingPassword());
if(!isValid){
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addPropertyNode( "matchingPassword" ).addConstraintViolation();
}
return isValid;
}
it will bind the validation result to your 'matchingPassword' attribute. So in your thymeleaf template us it like this:
${#fields.errors("matchingPassword")}
Add this inside the form tag:
<p data-th-each="err : ${#fields.allErrors()}" data-th-text="${err}" class="error">
Invalid input.
</p>
<p th:if="${#fields.hasErrors('${yourObject}')}" th:errors="${yourObject}"></p>

How to use ValidationUtils in spring

I want to use ValidationUtils as follows. But I cannot instantiate errors object since Errors is an Interface. Can you tell me how I can make this working without using a validator?
if(visitorDetails==null)
{
Errors errors;
visitorDetails=new Visitor();
ValidationUtils.rejectIfEmpty(errors, "VisitorInfo", "Selected Visitor Details Not Found");
}
Read this : Validation...
However you must implement the Validation interface in a class, and than use it to validate your object, and to do that you autowire validator in your controller..
This is an example:
public class PersonValidator implements Validator {
/**
* This Validator validates *just* Person instances
*/
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}
and in your controller:
....
#Autowired
private PersonValidator personValidator;
#InitBinder
protected void initBinder(final HttpServletRequest request, final ServletRequestDataBinder binder) {
binder.addValidators(personValidator);
}
...
Assuming you are using Spring Boot.
If using application.properties (under project/src/resources) put the following in it:
spring.messages.basename=validation
Now put a validation.properties (under project/src/resources) and put the following (for example) in it:
NotEmpty=This field is required.
Your model (AppUser in this case) should have:
private String useremail;
getters/setters;
Create a component (Class) like this (example):
#Component
public class UserAddValidator implements Validator {
#Autowired
private UserService userService;
#Override
public boolean supports(Class<?> aClass) {
return AppUser.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
AppUser user = (AppUser) o;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "useremail", "NotEmpty");
}
}
The following goes to your controller:
#RequestMapping(value = "registration", method = RequestMethod.POST)
public String registration(#ModelAttribute("userForm") AppUser userForm, BindingResult bindingResult, Model model) {
useraddValidator.validate(userForm, bindingResult);
if (bindingResult.hasErrors()) {
return "userregistration";
}
userService.save(userForm);
model.addAttribute("success", "User " + userForm.getUsername() + " created successfully");
return "success";
}
Last but not the least, in your view put similar to this:
<div class="row">
<label class="col-md-3" for="useremail">Email-ID</label>
<div class="col-md-8">
<spring:bind path="useremail">
<div class="form-group ${status.error ? 'has-error' : ''}">
<form:input type="text" path="useremail" class="form-control"
placeholder="Email-id" autofocus="true"></form:input>
<form:errors path="useremail">${emailerror}</form:errors>
</div>
</spring:bind>
</div>
</div>
The result should look (something) like below:
You can look HERE for more validations.
If I understand your question correctly you want to get the errors object.
In your case I would suggest below approach.
if(visitorDetails==null)
{
visitorDetails=new Visitor();
Errors errors = new BeanPropertyBindingResult(visitorDetails, visitorDetails.getClass().getName());
ValidationUtils.rejectIfEmpty(errors, "VisitorInfo", "Selected Visitor Details Not Found");
}
Let me know if you need more help.
you can use it to make some constraint on some of your field like show error when the field is empty or emptywithspace , this class already contain some static method that can do that
below an exemple for using ValidationUtils class
public class UserValidator implements Validator {
public boolean supports(Class clazz) {
// TODO Auto-generated method stub
return Employee.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
// TODO Auto-generated method stub
ValidationUtils.rejectIfEmpty(errors, "email", "email");
ValidationUtils.rejectIfEmpty(errors, "password", "password");
Employee emplo = (Employee) target;
if(emplo.getEmail() != null && emplo.getEmail()=="aa") {
errors.rejectValue("email", "email invalide ");
}

Resources