Constraint validator old value - spring-boot

I am using hibernate validator within a spring boot application.
I want to create an annotation, which will be validating updateability of a field. Something like #Updatable(WHILE_NEW).
I have created custom hibernate validator according to hibernate docs.
public class UpdatableConstraintValidator implements ConstraintValidator<UpdatableConstraint, Object> {
Updatable updatableMode;
#Override
public void initialize(UpdatableConstraint updatableConstraint) {
this.updatableMode = updatableConstraint.value();
}
#Override
public boolean isValid(Object field, ConstraintValidatorContext cxt) {
...some logic...
return result;
}
}
My question is, if there is a way to find out whether the operation which is being validated is CREATE or UPDATE?
And if it is UPDATE, is there a way to find out the old value of validated field?

Don't know about Spring Boot but I can say for sure that you won't be able to do that inside the Hibernate Validator boundaries.
Hibernate Validator just sees the current state of a bean and has absolutely no idea of its lifecycle.

Related

Where to validate uniqueness of field in Spring/Hibernate

I am building a REST API using spring and hibernate. I have come across the issue where I want to create a user and want to know the best practice on how to validate that the user can be created.
My controller has the #Valid annotation on the User object that gets passed into the method, and this checks for valid structure, however there is no #Unique property that gets picked up by #Valid.
I am using the #Column(unique = true) but this throws an error at the persistence level and I feel like that is quite low level and makes it difficult to throw a custom UsernameAlreadyExistsException().
My question here is what is the best practice in terms of preforming this type of validation. I thought about creating a custom annotation but it seems quite messy especially because as the project grows I would need multiple validators for different fields and it also seems to be closley related to tying the service layer to the annotation which seems messy
In my opinion, using custom annotation is the best approach to do stuff like this, you can inject some bean in ConstraintValidator and perform validation. However you can try one of the below unusual approaches, maybe it will fit your requirements.
Spring AOP
Spring Handler Interceptor
JPA Event Listeners
It's just my opinion about this, in most cases I think I will create custom annotations to handle it.
A good practice would be to put validation both on the database (which we know nothing about, but it is not complicated really) and on the Spring's side.
As #kamil-w already said, a good is to write custom constraint validator, see here for an example.
Keep in mind that you can always pass parameters like to constraint annotation, and then access them in your ConstraintValidator, for example.:
#Entity
public class Member {
// ...
#UniqueField(fieldName = "login", context = Member.class)
private String login;
}
#Component
public class UniqueFieldValidator implements ConstraintValidator<UniqueField, Object> {
#PersistenceUnit
private EntityManagerFactory emf;
private Class validationContext;
private String fieldName;
#Override
public void initialize(UniqueField uniqueField) {
this.validationContext = uniqueField.validationContext();
this.fieldName = uniqueField.fieldName();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext cxt) {
// use value, this.validationContext, this.fieldName and entity manager to check uniqueness
}
}

Spring 5 Webflux functional endpoints - How to perform input validation?

According to the current doc (5.0.0.RELEASE) Spring Webflux supports validation when working with annotated controllers:
By default if Bean Validation is present on the classpath — e.g.
Hibernate Validator, the LocalValidatorFactoryBean is registered as a
global Validator for use with #Valid and Validated on #Controller
method arguments.
However nothing is said about how to automate it with functional endpoints. In fact, the only example of input processing in the documentation doesn't validate anything:
public Mono<ServerResponse> createPerson(ServerRequest request) {
Mono<Person> person = request.bodyToMono(Person.class);
return ServerResponse.ok().build(repository.savePerson(person));
}
Are we supposed to do this manually or there is some automatic way to do it?
In Spring version 5.0, there is no automatic way to do validation in functional endpoints, and as such validation must be done manually.
Though there are currently no concrete plans to do so, we might add some sort of validation in the future. But even then it will be an explicit method call, and not an automatic mechanism. Overall, the functional endpoint model is designed to be a lot more explicit than the annotation-based model.
As arjen-poutsma said, it seems there is no way of running automated validations on Spring 5 functional endpoints.
Spring documentation is not very clear about this, and it doesn't suggest any approach.
On this Baeldung article, you'll find an idea on how you can run validations using this approach (disclaimer: I'm the writer of the article :) )
In a nutshell, you can follow these steps:
Implement Spring Validators to evaluate your resources
Create an abstract class with the basic procedure that any handler will follow when processing a request, leaving up to the children classes what to do when the data is valid
Make your request handler classes extend this abstract class, implementing this abstract method, stating the body it will be expecting, and what validator needs to be used to validate it
EDIT:
I've been following this related Spring issue, and it seems we now count with official documentation regarding this subject: https://github.com/spring-projects/spring-framework/blob/master/src/docs/asciidoc/web/webflux-functional.adoc#validation
The suggested approach is to use validators as explained in the article.
At the current version(2.0.4.RELEASE) there isn't a way to do automatic validation with handles, however you always could make a manual validation like this:
#Slf4j
#Component
#FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
#RequiredArgsConstructor
public class MyHandlerValidator implements HandlerValidator<MyResource> {
Validator validator;
#Override
public void callValidator(final MyResource fdr) {
final DataBinder binder = new DataBinder(fdr);
binder.setValidator(validator);
binder.validate();
if (binder.getBindingResult().hasErrors()) {
final String reason = binder.getBindingResult().getFieldError().toString();
log.error(reason);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, reason);
}
}
}
The thing with this, its that the you should throw a WebExchangeBindException like automatic validation does, however i could't create a MethodParameter witch is a dependency to create this exception.
UPDATE:
Spring show us a way to do it, which is similar to my solution, but, not enough in my opinion on documentation
Just to demo some working code. If you need simple validation based on the object annotations like:
#Value
#Builder
#Jacksonized
public class SigninRequest {
#NotBlank(message = "The username is mandatory")
#Email(message = "The username should be valid Email")
String username;
#NotBlank(message = "The password is mandatory")
String password;
}
At the handler you need just one simple additional operator doOnNext:
#Component
#RequiredArgsConstructor
public class AuthHandler {
private final AuthService authService;
private final ObjectValidator validator;
public Mono<ServerResponse> signin(ServerRequest request) {
return ok().body(
request.bodyToMono(SigninRequest.class)
.doOnNext(validator::validate) //<-- just one single line
.flatMap(login -> authService.authenticate(login.getUsername(), login.getPassword())),
AuthResult.class);
}
}
The ObjectValidator is doing actual validation and throws the runtime exception with the 4xx error in case of validation errors:
#Component
#RequiredArgsConstructor
public class ObjectValidator {
private final Validator validator;
public <T> T validate(T object) {
var errors = validator.validate(object);
if (errors.isEmpty()) {
return object;
} else {
String errorDetails = errors.stream().map(er -> er.getMessage()).collect(Collectors.joining(", "));
throw new ObjectValidationException(errorDetails);
}
}
}
And the exception:
#ResponseStatus(code = HttpStatus.UNPROCESSABLE_ENTITY)
public class ObjectValidationException extends RuntimeException {
public ObjectValidationException(String errorDetails) {
super("Please supply the valid data: " + errorDetails);
}
}
If you properly setup global error handling you can keep you handler code clean and reuse the object validator across all your handlers.

#PathVariable Spring Controller

I'm working on a spring web application using thymeleaf as a view resolver lately, and when developping my controllers, i faced a new situation :
i'm not really sure about it but can the #Pathvariable passed from the view be an object ? (a compound key of a model to be precise ? )
i used ids before, but most of them were simple int ids, i just want to know if passing an object (which is the primary key of my object) is possible , and not simple int ids ?
And Thank you
You can use Spring PropertyEditor or Spring Converter see Spring Convertor
example
public class CategoryConverter implements Converter<String, Category>{
#Autowired
private CategoryService categoryService;
public Category convert(String id) {
return categoryService.findById(Long.valueOf(id));
}
}
But you may meet some problem when saving object directly to database.

JSR-303 bean validation with Spring and iBatis

Is it possible with JSR-303 bean validation to write a custom annotation that can talk to a back end service?
We accomplished this type of validation with the "old school" Spring validators. In that case, the validator was a Spring bean and could have other services injected into it. Then that validator is injected into the controller.
An example might be an annotation (perhaps #EmailExists) to verify if an email already exists. I can only do this with a SQL query using one of our services. I would prefer to "validate" this alongside the other annotations and check it as soon as possible and not have to explicity do it in a back end service.
NOTE: We are using iBatis/MyBatis so I can't use any JPA/Hibernate tricks :-)
thanks!
That's definitely possible. Spring provides dependency injection support also within constraint validators. So you can simply inject any required services in your custom validators like this:
public class EmailExistsValidator implements ConstraintValidator<EmailExists, String> {
#Inject
private EmailValidationService service;
#Override
public void initialize(EmailExists constraintAnnotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return service.exists(value);
}
}
Depending on your concrete scenario it might be a good idea to first check "cheap" constraints such as #NotNull and only if these constraints are valid check more expensive constraints such as #EmailExists.
You can do this with help of group sequences and a redefined default group sequence for your type.

Why is my Spring 3 Validator Validating Everything on the Model?

I have a spring 3 controller with a validator for one of the methods. It insists on validating every object on the model. Would anyone be able to explain to me why it does this or if I'm doing something wrong?
According to the docs, 5.7.4.3 Configuring a JSR-303 Validator for use by Spring MVC (http://static.springsource.org/spring/docs/3.0.0.RC3/spring-framework-reference/html/ch05s07.html)
With JSR-303, a single javax.validation.Validator instance typically validates all model objects that declare validation constraints. To configure a JSR-303-backed Validator with Spring MVC, simply add a JSR-303 Provider, such as Hibernate Validator, to your classpath. Spring MVC will detect it and automatically enable JSR-303 support across all Controllers.
Example:
#Controller
public class WhaleController {
#Autowired
private Validator myValidator;
#Autowired
private WhaleService whaleService;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(this.myValidator);
}
#RequestMapping(value="/save-the-whales")
#Transactional
public void saveTheWhales(#Valid WhaleFormData formData, BindingResult errors, Model model) {
if (!errors.hasFieldErrors()) {
Whale whale = new Whale();
whale.setBreed( formData.getBreed() );
this.whaleService.saveWhale( whale );
model.addAttribute("whale", whale);
}
model.addAttribute("errors", errors.getFieldErrors());
}
}
When run it will complain that Whale is an invalid target for myValidator (which is set to validate WhaleFormData, and does so fine). Whale is a POJO with no validation constraints, annotation and no config anywhere. Through trial and error I've found that ANY object placed on the model will attempt to be validated and fail if the validator is not setup to handle it. Primitives are just fine.
Can anyone tell me why this is, point me to the appropriate documentation and/or tell me the best way to put something on the model without having it validated?
In the case above I would like to place "whale" on the model as it will now have a unique whaleId() that it received from my persistence layer.
Thanks!
I guess this behaviour is not covered in the documentation well.
The problem is caused by the following:
By default, #InitBinder-annotated method is called for each non-primitive model attribute, both incoming and outcoming (the purpose of calling it for outcoming attibutes is to allow you to register custom PropertyEditors, which are used by form tags when rendering a form).
DataBinder.setValidator() contains a defensive check that call Validator.supports() and throws an exception if false is returned. So, there is no attempt to perform a validation, just an early check.
The solution is to restrict the scope of #InitBinder to particular attribute:
#InitBinder("whaleFormData")
protected void initBinder(WebDataBinder binder) { ... }

Resources