Spring custom validation annotation + Validator implementation - spring

I have a simple POJO which is considered valid if one of the 2 attributes (ssn / clientId) is populated. I have written a custom Spring Validator to do the validation.
The question I have is, is there a way I can annotate my POJO so that my custom Validator is invoked for validation automatically? I'm trying to avoid manually invoking it from multiple places.
public class QueryCriteria {
#NotNull
private QueryBy queryBy;
private String ssn;
private String clientId;
}
public class QueryCriteriaValidator implements org.springframework.validation.Validator {
#Override
public boolean supports(Class<?> clazz) {
return QueryCriteria.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
QueryCriteria criteria = (QueryCriteria) target;
switch (criteria.getQueryBy()){
case CLIENT:
//validation logic
break;
case SSN:
//validation logic
break;
default:
break;
}
}
}

With Spring MVC, just add the custom validator to your controller's data binder:
#Autowired
private QueryCriteriaValidator validator;
#InitBinder
public void initializeBinder(WebDataBinder binder) {
binder.addValidators(validator);
}
Otherwise, you'll want to inject the custom validator and call it yourself.
#Autowired
private QueryCriteriaValidator validator;
public void process(QueryCriteria criteria) {
check(validator.validate(criteria));
// Continue processing.
}

Só you can create your own validation. Example :
#RequiredParam
#RegexpValidation("^[\\p{IsLatin}]{2}[\\p{IsLatin}-]* [\\p{IsLatin}]{2}[\\p{IsLatin}- ]*$")
private String name;
Create a class validator e send the form that you want to validate:
Map<String, String> invalidFieldMap = Validator.validate(form);
if (invalidFieldMap.size() > 0)
{
LOGGER.warn("Invalid parameters");
}
Look this gist Validator.java https://gist.github.com/cmsandiga/cb7ab4d537ef4d4691f7

Related

JSON field Desrializing to lowercase in Spring Boot

I have a Spring Boot Controller -
#RestController
public class UserController {
#PostMapping
#ResponseStatus(CREATED)
public UserResponse register( #Valid #RequestBody UserRequest userRequest) {
//return ....
}
}
Below is UserRequest.java
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class UserRequest {
private String email;
//other property
}
I am sending below json in request body -
{
"email" : "TEST#Example.com",
//some other fields.
}
Sometime client send email in uppercase or in camel case so in userRquest I want to change value of email field to lowercase like test#example.com while de serializing to UserRequest Object.
Is there any easy way to do this. Can I introduce my own annotation like #ToLowerCase how I can create my own annotation and use that at field level in UserRequest.
There is no easy way just by introducing a new annotation #ToLowerCase,
because then you would also need to implement some annotation processor
for doing the real conversion work.
But you can achieve your goal in a slightly different way.
In your UserRequest class annotate the email property
with #JsonDeserialize and specify a converter there.
#JsonDeserialize(converter = ToLowerCaseConverter.class)
private String email;
You need to implement the converter class by yourself,
but it is easy by extending it from StdConverter.
public class ToLowerCaseConverter extends StdConverter<String, String> {
#Override
public String convert(String value) {
return value.toLowerCase();
}
}
Jackson will use the setter methods in your class.
Perform the conversion to lower case in the setter.
For example
public void setEmail(String newValue)
{
email = StringUtils.lowerCase(newValue);
}
StringUtils is an apache commons class.
You can make a general StringDeserializer and register it in ObjectMapper as shown below:-
StringDeserializer class
public final class StringDeserializer extends StdDeserializer<String> {
public StringDeserializer() {
super((Class<String>) null);
}
#Override
public String deserialize(JsonParser parser, DeserializationContext context) throws IOException {
JsonToken token = parser.getCurrentToken();
if (token == JsonToken.VALUE_STRING) {
String text = parser.getText();
return text == null ? null : text.toLowerCase().trim();
}
return null;
}
}
JacksonConfiguration class
#Configuration
public class JacksonConfiguration {
#Autowired
void mapper(ObjectMapper mapper) {
mapper.registerModule(initModule());
}
private Module initModule() {
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new StringDeserializer());
return module;
}
}
The above code makes jackson deserialize all strings to lowercase and trimmed.

How to use Method level validation

I'm trying to validate some parameters used in a method with javax.validation, but I'm having trouble doing it right.
This is my method:
ServiceResponseInterface getEngineTriage(
#NotNull(message = Constants.MANDATORY_PARAMETERS_MISSING) String riskAssessmentId,
#NotNull(message = Constants.MANDATORY_PARAMETERS_MISSING) String participantId,
#Pattern(regexp = "NEW|RENEWAL|EDIT|OPERATION|RATING", flags = Pattern.Flag.CASE_INSENSITIVE, message = Constants.WRONG_PARAMETERS) String eventType) {
~Some code~
return ServiceResponseNoContent.ServiceResponseNoContentBuilder.build();
}
The class has the #Validated annotation, at this point I'm stuck, how can I check when I call the method if the paramethers are validated?
Basically, if your configuration is right, your method is not executed if any validation error occurs. So you need to handle your method with a simple try-catch block.
I will give an example configuration for method level validation in Spring below.
public interface IValidationService {
public boolean methodLevelValidation(#NotNull String param);
}
#Service
#Validated
public class ValidationService implements IValidationService {
#Override
public boolean methodLevelValidation(String param) {
// some business logic here
return true;
}
}
And you can handle any validation errors like below:
#Test
public void testMethodLevelValidationNotPassAndHandle() {
boolean result = false;
try {
result = validationService.methodLevelValidation(null);
Assert.assertTrue(result);
} catch (ConstraintViolationException e) {
Assert.assertFalse(result);
Assert.assertNotNull(e.getMessage());
logger.info(e.getMessage());
}
}
Note: You need to define your validation annotations in your interface if you have implemented your component from one. Otherwise, you can just put it in your bare spring component:
#Component
#Validated
public class BareValidationService {
public boolean methodLevelValidation(#NotNull String param) {
return true;
}
}
Hope this helps, cheers!

How to server validate each entry in list using custom validator

I have a Springboot Rest application having a server custom validator for one of the model. There are 2 api endpoints, one receives single object which other receives list of same object. My custom validator works fine on first endpoint. How can i use same validator for other endpoint.
Model class
#Entity
#Table(name=TABLE_MESSAGE, schema = SCHEMA)
public class Message implements java.io.Serializable {
#Id #GeneratedValue(strategy=IDENTITY)
#Column(name=COLUMN_ID, unique=true)
private Long id;
#Basic(optional = false)
#Column(name = COLUMN_CREATETIMESTAMP, insertable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date timestamp;
#Column(name=COLUMN_MESSAGE_SENDERNAME)
private String senderName;
#Column(name=COLUMN_MESSAGE_SENDEREMAIL)
private String senderEmail;
#Column(name=COLUMN_MESSAGE_SUBJECT)
private String subject;
#Column(name=COLUMN_MESSAGE_BODY)
private String body;
}
DTO class
public class MessageForm {
private List<Message> messageList;
public List<Message> getMessageList() {
return messageList;
}
public void setMessageList(List<Message> messageList) {
this.messageList = messageList;
}
}
Custom validator
#Component
public class MessageValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Message.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "senderName", ERRORCODE_MESSAGE_SENDERNAME_EMPTY);
ValidationUtils.rejectIfEmpty(errors, "senderEmail", ERRORCODE_MESSAGE_SENDEREMAIL_EMPTY);
ValidationUtils.rejectIfEmpty(errors, "subject", ERRORCODE_MESSAGE_SUBJECT_EMPTY);
ValidationUtils.rejectIfEmpty(errors, "body", ERRORCODE_MESSAGE_BODY_EMPTY);
Message m = (Message) target;
if (!m.getSenderName().trim().equalsIgnoreCase(EMPTY_STRING) && m.getSenderName().matches(REGEX_CONTAINS_NUMBER)) {
errors.rejectValue("senderName", ERRORCODE_MESSAGE_SENDERNAME_INVALID);
}
if (!m.getSenderEmail().trim().equalsIgnoreCase(EMPTY_STRING) && !m.getSenderEmail().matches( REGEX_EMAIL)) {
errors.rejectValue("senderEmail", ERRORCODE_MESSAGE_SENDEREMAIL_INVALID);
}
}
}
Controller
#RestController
public class MainSiteRestController
{
#Autowired
private MessageValidator messageValidator;
#InitBinder("message")
protected void initMessageBinder(WebDataBinder binder) {
binder.addValidators(messageValidator);
}
// this works fine
public ResponseForm saveMessage(#Valid #RequestBody Message message, BindingResult bindingResult) throws APIException {
if (bindingResult.hasErrors()){
throw new APIException(getErrorMesage(bindingResult.getAllErrors()));
}
return apiService.saveMessage(message);
}
// this is not working
public ResponseForm saveAllMessage(#RequestBody MessageForm messageForm, Errors errors) throws APIException {
// need to validate the complete list or particular indexed object here, tried below code but not working
// messageValidator.validate(messageForm.getMessageList().get(0), errors);
if(errors.hasErrors()) {
throw new APIException(createErrorString(errors));
}
return apiService.saveAllMessage(messageForm);
}
}
Spring validators work on a single form, therefore you will have to create a validator for list dto.

How to inject PathVariable id into RequestBody *before* JSR-303 validation is executed?

I'm stuck in an apparently simple problem: I want to perform some custom validation based on the object id in a PUT request.
#RequestMapping(value="/{id}", method=RequestMethod.PUT)
public ResponseEntity<Void> update(#Valid #RequestBody ClientDTO objDto, #PathVariable Integer id) {
Client obj = service.fromDTO(objDto);
service.update(obj);
return ResponseEntity.noContent().build();
}
I'd like to create a custom validator to output a custom message in case I update some field that can't be the same of another object in my database. Something like this:
public class ClientUpdateValidator implements ConstraintValidator<ClientUpdate, ClientDTO> {
#Autowired
private ClientRepository repo;
#Override
public void initialize(ClientInsert ann) {
}
#Override
public boolean isValid(ClientDTO objDto, ConstraintValidatorContext context) {
Client aux = repo.findByName(objDto.getName());
if (aux != null && !aux.getId().equals(objDto.getId())) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("Already exists")
.addPropertyNode("name").addConstraintViolation();
return false;
}
return true;
}
}
However, the object id comes from #PathVariable, not from #RequestBody. I can't call "objDto.getId()" like I did above.
On the other hand, it doesn't make much sense to obligate to fill up the object id in the request body, because this way the path variable would become meaninless.
How can I solve this problem? Is there a way to inject the id from PathVariable into RequestBody object before bean validation is executed? If not, what would be a viable solution? Thanks.
Try to inject httpServletRequest into the custom validator
public class ClientUpdateValidator implements ConstraintValidator<ClientUpdate, ClientDTO> {
#Autowired
private HttpServletRequest request;
#Autowired
private ClientRepository repo;
#Override
public void initialize(ClientInsert ann) {
}
#Override
public boolean isValid(ClientDTO objDto, ConstraintValidatorContext context) {
// for example your path to put endpoint is /client/{id}
Map map = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
String id = map.get("id");
Client aux = repo.findByName(objDto.getName());
if (aux != null && !aux.getId().equals(id)) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("Already exists")
.addPropertyNode("name").addConstraintViolation();
return false;
}
return true;
}
}

Spring Rest Url ID Validation in DB - Eg: universities/{universityId}/campuses/{campusId}/buildings

I wanted to know the best practice of how to validate the ID of the path of my Rest API.
For example:
When I do a GET to retrieve a Building, I need to validate first if the {universityId} and {campusId} are actually valid (Existing in the DB) before proceeding.
Right now I have implemented a custom RepositoryValidation that provides those functionalities by throwing a ResourceNotFoundException() and those methods are called in my service class for the GET,PUT,POST..etc
Is there a better way to do the validation? I have read about Interceptors or Filters but not sure if that's the best practice.
Custom Exception:
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
super();
}
public ResourceNotFoundException(String message) {
super(message);
}
Repository Validation:
#Component
public class RepositoryValidation {
#Autowired
private UniversityRepository universityRepository;
#Autowired
private CampusRepository campusRepository;
#Autowired
private BuildingRepository buildingRepository;
public void checkIfUniversityExists(Long universityId){
if (!universityRepository.exists(universityId))
throw new ResourceNotFoundException("University with id: " + universityId + " not found");
}
public void checkIfCampusExists(Long campusId){
if (!campusRepository.exists(campusId))
throw new ResourceNotFoundException("Campus with id: " + campusId + " not found");
}
public void checkIfBuildingExists(Long buildingId){
if (!buildingRepository.exists(buildingId))
throw new ResourceNotFoundException("Building with id: " + buildingId + " not found");
}
}
Service:
#Service
public class BuildingService {
#Autowired
private BuildingRepository buildingRepository;
#Autowired
private RepositoryValidation repositoryValidation;
public Iterable<Building> list(Long campusId) {
return buildingRepository.findAllByCampusId(campusId);
}
#Transactional
public Building create(Building building) {
return buildingRepository.save(building);
}
public Building read(Long buildingId,Long campusId) {
repositoryValidation.checkIfCampusExists(campusId);
repositoryValidation.checkIfBuildingExists(buildingId);
return buildingRepository.findBuildingByIdAndCampusId(buildingId,campusId);
}
#Transactional
public Building update(Long buildingId,Building update) {
repositoryValidation.checkIfBuildingExists(buildingId);
Building building = buildingRepository.findOne(buildingId);
building.setBuildingName(update.getBuildingName());
return buildingRepository.save(building);
}
#Transactional
public void delete(Long buildingId,Long campusId) {
repositoryValidation.checkIfCampusExists(campusId);
repositoryValidation.checkIfBuildingExists(buildingId);
buildingRepository.deleteBuildingByIdAndCampusId(buildingId, campusId);
}
You should look into Springs' Validation-Beanvalidation.
With this, you can use #Valid to do simple validations on properties, for example:
#NotNull
#Size(max=64)
private String name;
You can also add the #Valid to inputs in a REST endpoint:
#RequestMapping("/foo", method=RequestMethod.POST)
public void processFoo(#Valid Foo foo) { /* ... */ }
For your needs, you could consider creating a custom #Constraint.
You would first create the constraint annotation:
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy=MyConstraintValidator.class)
public #interface MyConstraint {
}
And then the constraint validator:
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
#Autowired;
private Foo aDependency;
...
}
Notice you can inject other Spring beans into the ConstraintValidator as well.
Once implemented, this could easily be re-used and looks very concise.

Resources