Spring MVC and JSR-303 hibernate conditional validation - java

I've a form I want to validate. It contains 2 Address variables. address1 has always to be validated, address2 has to be validated based on some conditions
public class MyForm {
String name;
#Valid Address address1;
Address address2;
}
public class Address {
#NotEmpty
private String street;
}
my controller automatically validates and binds my form obj
#RequestMapping(...)
public ModelAndView edit(
#ModelAttribute("form")
#Valid
MyForm form,
BindingResult bindingResult,
...)
if(someCondition) {
VALIDATE form.address2 USING JSR 303
the problem is that if I use the LocalValidatorFactoryBean validator i can't reuse the BinidingResult object provided by Spring. The bind won't work as the target object of 'result' is 'MyForm' and not 'Address'
validate(form.getAddress2(), bindingResult) //won't work
I'm wondering what's the standard/clean approach to do conditional validation.
I was thinking in programmatically create a new BindingResult in my controller.
final BindingResult bindingResultAddress2 = new BeanPropertyBindingResult(address2, "form");
validate(form.getAddress2(), bindingResultAddress2);
but then the List of errors I obtain from bindingResultAddress2 can't be added to the general 'bindingResult' as the field names are not correct ('street' instead of 'address2.street') and the binding won't work.
Some dirty approach would be to extend BeanPropertyBindingResult to accept some string to append to the fields name.. do you have a better approach?

The standard approach for validating hierarchical structures is to use pushNestedPath()/popNestedPath(), though I'm not sure how it plays with JSR-303:
bindingResult.pushNestedPath("address2");
validate(form.getAddress2(), bindingResult);
bindingResult.popNestedPath();

I've never tried myself, but I think the correct approach is using validator groups.

First of all, let's see #javax.validation.Valid API
Mark an association as cascaded. The associated object will be validated by cascade.
When Spring framework uses #Valid as a marker to validate its command objects, it corrupts its purpose. Spring should instead create your own specific annotation which specifies the groups which should be validated.
Unfortunately, you should use Spring native Validator API if you need to validate some groups
public void doSomething(Command command, Errors errors) {
new BeanValidationValidator(SomeUserCase.class, OtherUserCase.class)
.validate(command, errors);
if(errors.hasErrors()) {
} else {
}
}
BeanValidationValidator can be implemented as
public class BeanValidationValidator implements Validator {
javax.validation.Validator validator = ValidatorUtil.getValidator();
private Class [] groups;
public BeanValidationValidator(Class... groups) {
this.groups = groups;
}
public void validate(Object command, Errors errors) {
Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(command, groups);
for(ConstraintViolation<Object> constraintViolation: constraintViolationSet) {
errors.rejectValue(constraintViolation.getPropertyPath().toString(), null, constraintViolation.getMessage());
}
}
}

Related

Get Custom Validation Errors into BindingResult

I'm trying to remove repeating code from my Spring Controllers, specifically - removing the need to execute the validator.validate(form, bindingResult) from the start of many of my functions.
I have a few classes that have corresponding validator classes that implement Spring's validator interface. I have searched around to try and find an answer but I'm having trouble finding one that really matches this.
Snippet of Person Form Class with annotated attributes
public class Person {
#Size(min=1, message="Name missing")
private String name;
#Size(min=1, message="Age missing")
private String age;
.... getters and setters etc.
Person Validator Class
#Component
public class PersonValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Person.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
errors.reject("No sir!");
}
}
Ideally, I'd like to be able to have all the errors contained within the BindingResult, including the errors from the validator class. So that when I use the #Validated annotation my BindingResult is fully populated with all the errors from both the simple annotations and the custom validator.
Desired Outcome
#RequestMapping(value="/save", method=RequestMethod.POST)
public #ResponseBody String save(#Validated #RequestBody Person personForm, BindingResult bindingResult, HttpServletRequest request)
{
bindingResult.getAllErrors(); <-- fully pop with annotation and custom validator errors
Instead of:
#RequestMapping(value="/save", method=RequestMethod.POST)
public #ResponseBody String save(#Validated #RequestBody Person personForm, BindingResult bindingResult, HttpServletRequest request)
{
personValidator.validate(person, bindingResult) <-- Populate bindingResult with customer validator errors, if any
bindingResult.getAllErrors();
Has anyone got any neat examples they can share to get around this?
Thanks!
You need to add the validator to the databinder for multiple validators to work. In your code add an #InitBinder method and add the PersonValidator to the WebDataBinder.
#InitBinder("personForm")
public void initBinder(WebDataBinder wdb) {
wdb.addValidators(personValidator);
}
Will bind a validator to the personForm model object.
This will configure a global rule that this validator is applied to all bindings/conversions. If you want to limit this to a certain model you can specify the name of the model in the #InitBinder.
#InitBinder
public void initBinder(WebDataBinder wdb) {
wdb.addValidators(personValidator);
}
As possible solution, you can define your own custom annotation and CustomConstraintValidator that will implement interface ConstraintValidator<A extends Annotation, T>.
At the end BindingResult will contain either default validator and your custom validator errors.
Here is a good example. If I understand your question correctly of course.

Spring-MVC form validation highlight input field after DAO validation

I'm converting Struts 1.3 project to Spring. Instead of struts form fields, I'm using spring form.
I have used ActionErrors in struts to highlight the field using errorStyleClass attribute.
Similarly, in spring cssErrorClass is available. But, How to use it after the dao validation?
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#ModelAttribute("login") #Validated Login login, BindingResult result, Model model) {
if (result.hasErrors()) {
//THIS VALIDATION DONE BY ANNOTATION AND HIGHLIGHTING THE FIELD
//USING "cssErrorClass"
return HOMEPAGE;
}
boolean checkAuthentication = authService.checkAuthentication(login);
if(!checkAuthentication){
// HOW TO SET THE ERROR HERE?
// Is there any way to set the error like
// error.setMessage("userId","invalid.data");
// so that, is it possible to display error message by
// highlighting the fields using "cssErrorClass"?
}
return HOMEPAGE;
}
You need to annotate your entities using Java Bean Validation framework JSR 303, like this
public class Model{
#NotEmpty
String filed1;
#Range(min = 1, max = 150)
int filed2;
....
}
And add #Valid to your controller, like this
public class MyController {
public String controllerMethod(#Valid Customer customer, BindingResult result) {
if (result.hasErrors()) {
// process error
} else {
// process without errors
}
}
You can find more examples for it here and here
EDIT:
If you want to register more errors based on custom validation steps in code, you can use rejectValue() method in the BindingResult instance, like this:
bindingResult.rejectValue("usernameField", "error code", "Not Found username message");

Convert JSR-303 validation errors to Spring's BindingResult

I have the following code in a Spring controller:
#Autowired
private javax.validation.Validator validator;
#RequestMapping(value = "/submit", method = RequestMethod.POST)
public String submitForm(CustomForm form) {
Set<ConstraintViolation<CustomForm>> errors = validator.validate(form);
...
}
Is it possible to map errors to Spring's BindingResult object without manually going through all the errors and adding them to the BindingResult? Something like this:
// NOTE: this is imaginary code
BindingResult bindingResult = BindingResult.fromConstraintViolations(errors);
I know it is possible to annotate the CustomForm parameter with #Valid and let Spring inject BindingResult as another method's parameter, but it's not an option in my case.
// I know this is possible, but doesn't work for me
public String submitForm(#Valid CustomForm form, BindingResult bindingResult) {
...
}
A simpler approach could be to use Spring's abstraction org.springframework.validation.Validator instead, you can get hold of a validator by having this bean in the context:
<bean id="jsr303Validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
#Autowired #Qualifier("jsr303Validator") Validator validator;
With this abstraction in place, you can use the validator this way, passing in your bindingResult:
validator.validate(obj, bindingResult);
Spring uses a SpringValidatorAdapter to convert javax.validation.ConstraintViolation objects to ObjectError or FieldError objects, as found in the binding result.
The BindStatus then uses a message source (like the web application context itself) to translate the errors.
In short, you could do:
SpringValidatorAdapter springValidator = new SpringValidatorAdapter(validator);
BindingResult bindingResult= new BeanPropertyBindingResult(myBeanToValidate, "myBeanName");
springValidator.validate(myBeanToValidate, bindingResult);
This is easier when writing a unit test, because you don't even need to create a Spring context.
Expanding on Kristiaan's answer, for testing purposes it is not necessary to create a spring context to validate using Spring's bindingResult. The following is an example:
public class ValidatorTest {
javax.validation.Validator javaxValidator = Validation.buildDefaultValidatorFactory().getValidator();
org.springframework.validation.Validator springValidator = new SpringValidatorAdapter(javaxValidator);
#Test
public void anExampleTest() {
JSR303AnnotatedClassToTest ctt = new JSR303AnnotatedClassToTest( ..init vars..)
... test setup...
WebDataBinder dataBinder = new WebDataBinder(ctt);
dataBinder.setValidator(springValidator);
dataBinder.validate();
BindingResult bindingResult = dataBinder.getBindingResult();
... test analysis ...
}
}
This approach doesn't require creating a binding result ahead of time, the dataBinder builds the right one for you.
#RequestMapping(value = "/submit", method = RequestMethod.POST)
public String submitForm(CustomForm form) {
Set<ConstraintViolation<CustomForm>> errors = validator.validate(form);
BindingResult bindingResult = toBindingResult(errors, form, "form");
...
}
private BindingResult toBindingResult(ConstraintViolationException e, Object object, String objectName) {
BindingResult bindingResult = new BeanPropertyBindingResult(object, objectName);
new AddConstraintViolationsToErrors().addConstraintViolations(e.getConstraintViolations(), bindingResult);
return bindingResult;
}
private static class AddConstraintViolationsToErrors extends SpringValidatorAdapter {
public AddConstraintViolationsToErrors() {
super(Validation.buildDefaultValidatorFactory().getValidator()); // Validator is not actually used
}
#SuppressWarnings({"rawtypes", "unchecked"})
public void addConstraintViolations(Set<? super ConstraintViolation<?>> violations, Errors errors) {
// Using raw type since processConstraintViolations specifically expects ConstraintViolation<Object>
super.processConstraintViolations((Set) violations, errors);
}
}
Unlike the other answers to this question, this solution handles the case where there already exists a Set<ConstraintViolation<?>> which needs to be converted to to a BindingResult.
Explanation
Spring provides the SpringValidatorAdapter class to perform bean validations, storing the results in an Errors instance (note that BindingResult extends Errors). The normal manual use of this class would be to use it to perform the validations via the validate method:
Validator beanValidator = Validation.buildDefaultValidatorFactory().getValidator();
SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(beanValidator);
BindException bindException = new BindException(form, "form");
validatorAdapter.validate(form, bindException);
However, this doesn't help in the case where there already exists a Set<ConstraintViolation<?>> which needs to be converted to a BindingResult.
It is still possible to achieve this goal, though it does require jumping through a couple extra hoops. SpringValidatorAdapter contains a processConstraintViolations method which converts the ConstraintViolation objects into the appropriate Spring ObjectError subtypes, and stores them on an Errors object. However, this method is protected, limiting its accesibility to subclasses.
This limitation can be worked around by creating a custom subclass of SpringValidatorAdapter which delegates to or exposes the protected method. It is not a typical usage, but it works.
public class AddConstraintViolationsToErrors extends SpringValidatorAdapter {
public AddConstraintViolationsToErrors() {
super(Validation.buildDefaultValidatorFactory().getValidator()); // Validator is not actually used
}
#SuppressWarnings({"rawtypes", "unchecked"})
public void addConstraintViolations(Set<? super ConstraintViolation<?>> violations, Errors errors) {
// Using raw type since processConstraintViolations specifically expects ConstraintViolation<Object>
super.processConstraintViolations((Set) violations, errors);
}
}
This custom class can be used to populate a newly created BindingResult, achieving the goal of creating a BindingResult from a Set<ConstraintViolation<?>>.
private BindingResult toBindException(ConstraintViolationException e, Object object, String objectName) {
BindingResult bindingResult = new BeanPropertyBindingResult(object, objectName);
new AddConstraintViolationsToErrors().addConstraintViolations(e.getConstraintViolations(), bindingResult);
return bindingResult;
}
I've encountered a similar issue and this is how I resolved it.
Given your example, this is how I implemented it
First, I used a smart validator, and in the method I let spring inject the BindingResult
#Autowired
private org.springframework.validation.SmartValidator validator;
#RequestMapping(value = "/submit", method = RequestMethod.POST)
public String submitForm(CustomForm form, BindingResult bindingResult) {
Set<ConstraintViolation<CustomForm>> errors = validator.validate(form);
...
}
And then using that binding result i pass it in the SmartValidator so that any errors will be bounded to BindingResult.
validator.validate(form, bindingResult);
if(bindingResult.hasErrors()) {
throw new BindException(bindingResult);
}

Spring Controller: use domain objects as #RequestBody

I have a domain object class User (it is a JPA entity):
#Entity
public class User {
private String name;
private boolean enabled = true;
// getters/setters
}
And I am trying to offer a REST API to allow clients to create new users, using Spring 3 MVC:
#Controller
public class UserController {
#RequestMapping(value="/user", method=RequestMethod.POST)
#ResponseBody
public String createRealm(#RequestBody User user) {
user.setEnabled(true); // client is not allowed to modify this field
userService.createUser(user);
...
}
}
It works great, but I do not know if it is a good idea to use the domain objects as #RequestBody, because I have to protect some fields that should not be directly modified by the client (i.e. "enabled" in this case).
What are the pros/cons of these alternatives:
Use the domain objects and protect the fields the user is not allowed to modify (for example set them to null or to its default value by hand)
Use a new set of auxiliar objects (something similar to a DTO), such as a UserRequest that only contains the fields I want to expose through the REST API, and map them (i.e. with Dozer) to the domain objects.
The second alternative looks like this:
#Entity
public class User {
private String name;
private boolean enabled = true;
// getters/setters
}
public class UserRequest {
private String name;
// enabled is removed
// getters/setters
}
#Controller
public class UserController {
#RequestMapping(value="/user", method=RequestMethod.POST)
#ResponseBody
public String createRealm(#RequestBody UserRequest userRequest) {
User user = ... // map UserRequest -> User
userService.createUser(user);
...
}
}
Is there any other way that avoids code duplication and is easier to maintain?
There is another option - you can disallow the submission of a given set of properties, using the DataBinder.setDisallowedFields(..) (or using .setAllowedFields(..))
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields(..);
}
This is fine if you have one or two properties that differ.
Otherwise, having a special object (like ProfileDetails or UserRequest) makes more sense. I am using such a DTO-like object for this scenario and then transfer the fields with BeanUtils.copyProperties(..) from commons-beanutils
A third, perhaps better option, is to put all profile-related fields into a separate entity (mapped with #OneToOne with user) or to an #Embeddable object, and use it instead.

Validation in Spring MVC

We are using Spring MVC 3.0 in our web application. We are also using the validation framework of Spring MVC.
While doing validation we need to create our validators for each entity we need to validate. For example if I have a Person entity, I will validate it using following PersonValidator.
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.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "age", "field.required");
}
}
My question is, is it possible to have different validations for same entities for different methods.
#RequestMapping(method = RequestMethod.POST)
public String add(#Valid Person person, BindingResult result) {
if (result.hasErrors()) {
return "person/new";
}
personService.addPerson(person);
return "redirect:/persons";
}
#RequestMapping(method = RequestMethod.POST)
public String update(#Valid Person person, BindingResult result) {
if (result.hasErrors()) {
return "person/edit";
}
personService.updatePerson(person);
return "redirect:/persons";
}
I want to validate first name, last name and age while adding a new person but while updating I don't want age to be mandatory.
This is just a random situation, it can be any entity and any property.
How do we handle such situations?
Thanks.
You could drop the #Valid annotation and instead inside the method set a flag on your model object for insert vs update and then call the Validator directly (you can inject them into the controller).
Then inside the validator you can choose which validations are required for your current scenario.

Categories