0

I've been reading a lot about spring request validation. I read a lot articles on how to appropriately implement that, but I have some problem. This is my code:

RestController:

    @Autowired
EmployeeManager employeeManager;

@Autowired
EmployeeValidator employeeValidator;

@InitBinder("employee")
public void setupBinder(WebDataBinder binder) {
    binder.addValidators(employeeValidator);
}

// -------------- CREATE EMPLOYEES --------------

@PostMapping(value = "add")
public ResponseEntity<EmployeeDTO> addEmployee(@Valid @RequestBody EmployeeDTO employee) {

    boolean isCreated = employeeManager.addEmployee(employee);

    if(isCreated) {
        return new ResponseEntity<>(employee, HttpStatus.CREATED);
    }

    return new ResponseEntity(new CustomError("Unable to create, employee with email " +
            employee.getEmail() + " already exist."), HttpStatus.CONFLICT);
}

Validator:

    package com.employee.api.EmployeeAPI.validator;


import com.employee.api.EmployeeAPI.model.dto.EmployeeDTO;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class EmployeeValidator implements Validator {

    private Pattern pattern;
    private Matcher matcher;

    private static final String STRING_PATTERN = "[a-zA-Z]+";
    private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
            + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";

    @Override
    public boolean supports(Class<?> clazz) {
        return EmployeeDTO.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        EmployeeDTO employee = (EmployeeDTO) target;
        if (validateInputString(employee.getFirstName(), STRING_PATTERN)) {
            errors.rejectValue("firstName", "firstName.invalid");
        }

        if (validateInputString(employee.getLastName(), STRING_PATTERN)) {
            errors.rejectValue("lastName", "lastName.invalid");
        }

        if (validateInputString(employee.getJob(), STRING_PATTERN)) {
            errors.rejectValue("job", "job.invalid");
        }

        if (validateInputString(employee.getEmail(), EMAIL_PATTERN)) {
            errors.rejectValue("email", "email.invalid");
        }
    }

    private boolean validateInputString(String input, String regexPattern) {
        pattern = Pattern.compile(regexPattern);
        matcher = pattern.matcher(input);

        return (!matcher.matches() || input == null || input.trim().length() == 0);
    }

}

and in config I added bean:

@Bean
public EmployeeValidator beforeAddOrUpdateEmployeeValidator() {
    return new EmployeeValidator();
}

I am not really sure of how it should be invoked right now when adding employees, because it surely does not work for now. Could you help me with the right implementation or point in the right direction?

1 Answer 1

1

I'm not familiar with org.springframework.validation.Validator, but will suggest you how to do the same validation as you need with javax.validation.ConstraintValidator (JSR-303). Your controller class is fine and no changes needed there.

you need to create a custom annotation @ValidEmployee and annotate your dto with it:

@ValidEmployee
public class EmployeeDto {
    ...
}

ValidEmployee annotation:

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = EmployeeValidator.class)
@Documented
public @interface ValidEmployee {
    String message() default "{ValidEmployee.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

and implement your validation logic in isValid method:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class EmployeeValidator implements ConstraintValidator<ValidEmployee, EmployeeDto> {

    @Override
    public void initialize(ValidEmployee constraintAnnotation) {
    }

    @Override
    public boolean isValid(EmployeeDto employee, ConstraintValidatorContext context) {
            // do your validation logic
    }

}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for this answer. I see that the validator bean is created during the request. Can I execute this validator at startup as part of optimization? I am trying to reduce the response time taken for the query.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.