Dynamic POJO validation based on groups in spring

Adit A. Pillai :

Consider the following pojo for reference:

public class User{

    private  String username;
    private String firstName;
    private String middleName;
    private String lastName;
    private String phone;

    //getters and setters

}

My application is a basically spring-boot based REST API which exposes two endpoints, one to create the user and the other to retrieve a user.

The "users" fall into certain categories, group-a, group-b etc. which I get from the headers of the post request.

I need to validated the user data in runtime and the validations may differ based on the group of a user.

for example, the users that fall into group-a may have phone numbers as an optional field whereas it might be a mandatory field for some other group.

The regex may also vary based on their groups.

I need to be able to configure spring, to somehow dynamically validate my pojo as soon as they are created and their respective set of validations get triggered based on their groups.

Maybe I can create a yml/xml configuration which would allow me to enable this?

I would prefer to not annotate my private String phone with @NotNull and @Pattern.

My configuration is as follows:

public class NotNullValidator implements Validator {
    private String group;
    private Object target;

    public String getGroup() {
        return group;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public void validate(Object o) {
        if (Objects.nonNull(o)) {
            throw new RuntimeException("Target is null");
        }
    }
}


public interface Validator {
    void validate(Object o);
}


@ConfigurationProperties(prefix = "not-null")
@Component
public class NotNullValidators {
    List<NotNullValidator> validators;

    public List<NotNullValidator> getValidators() {
        return validators;
    }

    public void setValidators(List<NotNullValidator> validators) {
        this.validators = validators;
    }
}


application.yml

not-null:
  validators:

    -
      group: group-a
      target: user.username

    -
      group: group-b
      target: user.phone

I want to configure my application to somehow allow the validators to pick their targets (the actual objects, not the strings mentioned in the yml), and invoke their respective public void validate(Object o) on their targets.

P.S.

Please feel free to edit the question to make it better.

I am using jackson for serializing and deserializing JSON.

ricol070 :

The easiest solution to your problem, as i see it, is not with Spring or the POJOs themselves but with a design pattern.

The problem you're describing is easily solved by a strategy pattern solution.

You match the strategy to use by the header you're expecting in the request, that describes the type of user, and then you perform said validations inside the strategy itself.

This will allow you to use the same POJO for the whole approach, and deal with the specifics of handling/parsing and validating data according to the each type of user's strategy.

Here's a link from wiki books with a detailed explanation of the pattern

Strategy Pattern

Suppose you have a basic interface for your strategies:

interface Strategy { 

    boolean validate(User user);
}

And you have 2 different implementations for the 2 different types of user:

public class StrategyA implements Strategy {

    public boolean validate(User user){

         return user.getUsername().isEmpty();
    }
}

public class StrategyB implements Strategy {

    public boolean validate(User user){

         return user.getPhone().isEmpty();
    }
}

You add a Strategy attribute to your User POJO and assign the right implementation of the Strategy to that attribute when you receive the post request.

Everytime you need to validate data for that user you just have to invoke the validate method of the assigned strategy.

If each User can fit multiple strategies, you can add a List<Strategy> as an attribute instead of a single one.

If you don't want to change the POJO you have to check which is the correct strategy every time you receive a post request.

Besides the validate method you can add methods to handle data, specific to each strategy.

Hope this helps.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=434335&siteId=1