How to conditionally invoke spring validators - java

I have a model like below. Existing code already has validations on individual properties. Now I have a requirement to ignore existing validations on billing_country and address if buyer.method is foo. I was thinking I could have a custom validator at buyer level, check for method and invoke validations only when buyer.method!=foo. Is this a valid approach? Are there any better alternatives?
"buyer": {
"method": "foo",
"instruments": [
{
"card": {
"type": "MASTERCARD",
"number": "234234234234234",
"expire_month": "12",
"expire_year": "2017",
"billing_country" : "US"
"Address" : {
"line1": "summer st",
"city": "Boston"
"country": "US"
}
}
}
]
}

There's two ways to do this.
You can either
create a custom validation annotation and validator which checks the value of method and then applies to appropriate validations to the other fields
use validation groups, where you manually check the value of method, then choose which group to use; your annotated fields will then need to be changed to only be applied when that group is active.
Option #1
Define a class-level annotation:
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = { MethodAndInstrumentsValidator.class })
#Documented
public #interface ValidMethodAndInstruments {
String message() default "{my.package.MethodAndInstruments.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Define a validator:
public class MethodAndInstrumentsValidator
implements ConstraintValidator<ValidMethodAndInstruments, Buyer> {
public final void initialize(final ValidMethodAndInstruments annotation) {
// setup
}
public final boolean isValid(final Buyer value,
final ConstraintValidatorContext context) {
// validation logic
if(!"foo".equals(value.getMethod())) {
// do whatever conditional validation you want
}
}
}
Annotate the buyer class:
#ValidMethodAndInstruments
public class Buyer { }
Option #2
Here, you'll have to invoke the validation manually.
First, define the validation groups:
public interface FooValidation {}
public interface NotFooValidation {}
Configure a validator:
#Bean
public LocalValidatorFactoryBean validatorFactory() {
return new LocalValidatorFactoryBean();
}
In your controller(?), check the value of method and do the validation:
#Autowired
private Validator validator;
// ...
public void validate(Object a, Class<?>... groups) {
Set<ConstraintViolation<Object>> violations = validator.validate(a, groups);
if(!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
// ...
validate(buyer, "foo".equals(buyer.getMethod())
? FooValidation.class
: NotFooValidation.class);
Finally, modify the groups in your model/dto class:
public class Buyer {
// ...
#Null(groups = FooValidation.class)
#NotNull(groups = NotFooValidation.class)
protected String billingCountry;
}

#beerbajay answer helped to an extent, so I'm going to accept his answer. However I also wanted to jot down a couple of problems we ran into approach he suggested. We couldn't use #Null and #NotNull annotation on address since it could be null. Secondly since we had to wrap this in a custom Constraint validator, therefore for NotFooValidation.class we were getting extra validation message string that was not desired. Lastly we were not able to use groups in #Valid which was already present on Address. We tried #Validated but that didn't work for some reason. We ended up implementing ReaderInterceptor to intercept request and manually call validations that were needed. Hope this helps someone down the road.

Related

Validate #RequestParam to be in a list of enum

I want to limit the user of Spring MVC to access only certain values of the enum, I need to throw constraint exception when the requested param contains the restricted value.
Enum Example:
public enum EnumActionValues {
WAIT,
OFFLINE,
LOGGED_IN,
LOGGED_OUT,
OTHERS,
//
;
public List<EnumActionValues> getManuallyAllowedActions() {
return Arrays.asList(
WAIT,
OFFLINE,
OTHERS
);
}
}
In the above enum I want to webrequest to the Controller should contain only getManuallyAllowedActions, the LOGGED_IN and LOGGED_OUT shouldn't be allowed by user, which will be used internally.
Is there any direct annotations to be used with #Valid/#Validated.
You can have a custom annotation and a validator that goes with it.
Your annotation could look like this:
#Documented
#Constraint(validatedBy = YourConstraintValidator.class)
#Target( { ElementType.FIELD } )
#Retention(RetentionPolicy.RUNTIME)
public #interface YourConstraint
{
String message() default "Invalid enum";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And your validator would be:
public class YourConstraintValidator implements ConstraintValidator<YourConstraint, EnumActionValues> {
#Override
public void initialize(YourConstraint constraint) {
}
#Override
public boolean isValid(EnumActionValues obj, ConstraintValidatorContext context) {
return obj == null || obj.getManuallyAllowedActions().contains(obj);
}
}
This validator allows for the enum to be null so it will still work in case the enum is null in the request.
Note that you will have to use #ModelAttribute annotation instead of #RequestParam for this to work.
I think your requirement here is very specific and you probably have to write the check yourself. Something like this should do the trick:
public ResponseEntity someEndpoint(final EnumActionValues aAction) {
if ((aAction != null) && !EnumActionValues.getManuallyAllowedActions().contains(aAction)) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
...
}

Using NotNull Annotation in method argument

I just started using the #NotNull annotation with Java 8 and getting some unexpected results.
I have a method like this:
public List<Found> findStuff(#NotNull List<Searching> searchingList) {
... code here ...
}
I wrote a JUnit test passing in the null value for the argument searchingList. I was expecting some type of error to happen but it went through as though the annotation was not there. Is this expected behavior? From what I understood, this was to allow you to skip writing the boilerplate null check code.
An explanation of what exactly #NotNull is supposed to do would be greatly appreciated.
#Nullable and #NotNull do nothing on their own. They are supposed to act as Documentation tools.
The #Nullable Annotation reminds you about the necessity to introduce an NPE check when:
Calling methods that can return null.
Dereferencing variables (fields, local variables, parameters) that can be null.
The #NotNull Annotation is, actually, an explicit contract declaring the following:
A method should not return null.
A variable (like fields, local variables, and parameters) cannot should not hold null value.
For example, instead of writing:
/**
* #param aX should not be null
*/
public void setX(final Object aX ) {
// some code
}
You can use:
public void setX(#NotNull final Object aX ) {
// some code
}
Additionally, #NotNull is often checked by ConstraintValidators (eg. in spring and hibernate).
The #NotNull annotation doesn't do any validation on its own because the annotation definition does not provide any ConstraintValidator type reference.
For more info see:
Bean validation
NotNull.java
Constraint.java
ConstraintValidator.java
As mentioned above #NotNull does nothing on its own. A good way of using #NotNull would be using it with Objects.requireNonNull
public class Foo {
private final Bar bar;
public Foo(#NotNull Bar bar) {
this.bar = Objects.requireNonNull(bar, "bar must not be null");
}
}
To make #NonNull active you need Lombok:
https://projectlombok.org/features/NonNull
import lombok.NonNull;
Follow: Which #NotNull Java annotation should I use?
If you are using Spring, you can force validation by annotating the class with #Validated:
import org.springframework.validation.annotation.Validated;
More info available here:
Javax #NotNull annotation usage
You could also use #NonNull from projectlombok (lombok.NonNull)
SO #NotNull just is a tag...If you want to validate it, then you must use something like hibernate validator jsr 303
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<List<Searching>> violations = validator.validate(searchingList);
I do this to create my own validation annotation and validator:
ValidCardType.java(annotation to put on methods/fields)
#Constraint(validatedBy = {CardTypeValidator.class})
#Documented
#Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface ValidCardType {
String message() default "Incorrect card type, should be among: \"MasterCard\" | \"Visa\"";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And, the validator to trigger the check:
CardTypeValidator.java:
public class CardTypeValidator implements ConstraintValidator<ValidCardType, String> {
private static final String[] ALL_CARD_TYPES = {"MasterCard", "Visa"};
#Override
public void initialize(ValidCardType status) {
}
public boolean isValid(String value, ConstraintValidatorContext context) {
return (Arrays.asList(ALL_CARD_TYPES).contains(value));
}
}
You can do something very similar to check #NotNull.
To test your method validation in a test, you have to wrap it a proxy in the #Before method.
#Before
public void setUp() {
this.classAutowiredWithFindStuffMethod = MethodValidationProxyFactory.createProxy(this.classAutowiredWithFindStuffMethod);
}
With MethodValidationProxyFactory as :
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
public class MethodValidationProxyFactory {
private static final StaticApplicationContext ctx = new StaticApplicationContext();
static {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.afterPropertiesSet(); // init advisor
ctx.getBeanFactory()
.addBeanPostProcessor(processor);
}
#SuppressWarnings("unchecked")
public static <T> T createProxy(T instance) {
return (T) ctx.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(instance, instance.getClass()
.getName());
}
}
And then, add your test :
#Test
public void findingNullStuff() {
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> this.classAutowiredWithFindStuffMethod.findStuff(null));
}
I resolved it with
#JsonSetter(nulls = Nulls.AS_EMPTY)
#NotBlank
public String myString;
Request Json:
{
myString=null
}
Response:
error must not be blank

How to set default group in bean validation context

I'm working with bean validations and I'm searching for a possibility to set a default group of my own bean validation annotation.
I have something (working) like this:
Application.class (calling validate on MyBean)
public class Application {
public static void main(String[] args) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<MyBean>> violations =
validator.validate(new MyBean(), SecondStep.class);
}
}
MyBean.class (the bean itself; here is what I want to prevent)
public class MyBean {
// I don't want to write this "groups" attribute every time, because it's very clear,
// that this should only be validated for the second step, isn't it?
#RequiredBySecondStep(groups=SecondStep.class)
private Object myField;
}
RequiredBySecondStep.class (the bean validation annotation)
#Documented
#Target(FIELD)
#Retention(RUNTIME)
#Constraint(validatedBy = RequiredBySecondStepValidator.class)
public #interface RequiredBySecondStep {
String message() default "may not be null on the second step";
Class<?>[] groups() default {}; // <-- here I want to set SecondStep.class
Class<? extends Payload>[] payload() default {};
}
RequiredBySecondStepValidator.class (an implemented constraint validator)
public class RequiredBySecondStepValidator implements ConstraintValidator<RequiredBySecondStep, Object> {
public void initialize(RequiredBySecondStep constraintAnnotation) {
}
public boolean isValid(Object object, ConstraintValidatorContext constraintContext) {
return object != null;
}
}
SecondStep.class (the bean validation group)
public interface SecondStep {
}
Unfortunately, it's not possible by specification, to set the default group in the RequiredBySecondStep annotation like this:
Class<?>[] groups() default SecondStep.class;
// and using just the following in the bean:
#RequiredBySecondStep
private Object myField;
This will result in a RuntimeException:
javax.validation.ConstraintDefinitionException: Default value for
groups() must be an empty array
Furthermore, there is not only a SecondStep. There are probably 5 different groups which I want to annotate directly with a #RequiredByFirstStep or #RequiredByFifthStep.
Is there a good way to implement this?
I think you got it all a bit wrong. There is indeed to way to do what you want and that's because the aspect of constraints and their validation via a ConstraintValidator is orthogonal to groups and groups sequences. Per design a constraint (annotation and its validator) should be independent of the group getting validated. Even if you would get this to work, it would not be portable constraints. Personally, I would re-think what you want to achieve. #RequiredByFirstStep does not tell you what the requirement is. You should develop constraints which imply what they are valiating (a string length, not null, etc), when or better in which condition they are executed is a then controlled by group interfaces.

Bean validation: at least one element of a list must have one field filled

I have a list of lets say Car objects. Each car has a miles member.
I need to validate (using Hibernate Validator) that at least one car in my list has a not null miles member. The best solution would be an annotation that would apply to all the elements of a collection, but would validate in the context of the whole collection.
I have two questions:
Is there already an annotation for this (I do not know of any)?
If there is no annotation for this, is there a way to create an generic annotation?
I thought of specifiyng the name of the field that has to be not null at least for one element in the list, then I can apply this not only for Car classes.
public class VechicleTransport {
#AtLeastOneNotNull( fieldName = "miles" )
private List<Car> carList;
}
public class Car {
private Double miles;
....
}
AFAIK there is no such annotation, You need to define custom constraint annotations and define your validation logic inside it.
Like
Defining custom constraint annotation AtLeastOneNotNull
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy=AtLeastOneNotNullValidator.class)
public #interface AtLeastOneNotNull{
String message() default "Your error message";
Class<!--?-->[] groups() default {};
Class<!--? extends Payload-->[] payload() default {};
}
Defining validator for custom annotation.
public class AtLeastOneNotNullValidator implements ConstraintValidator<AtLeastOneNotNull, object=""> {
#Override
public void initialize(AtLeastOneNotNull constraint) {
}
#Override
public boolean isValid(Object target, ConstraintValidatorContext context) {
// Add logic to check if atleast one element have one field
}
}
Link for more details

Spring MVC: How to perform validation?

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.

Categories