400 Bad request with Hibernate #Valid - spring

I have a strange behaviour when I validate my form.
As soon as I add the Hibernate #Valid annotation, Tomcat consided my request as "bad" if the posted data are not valid. If the data are valid, no worries.
I use:
Tomcat 7.0.52
Javax Validation api 1.1.0.Final
Hibernate Validator 5.1.0.Final
Spring 4.0.3.RELEASE
At the moment, I do a really simple validation:
public class RemoveCacheElementForm {
#NotBlank(message = "Please select a cache name.")
private String name;
#NotBlank(message = "Please select a cache entry key.")
private String key;
The Spring controller:
/**
* Handler to remove one cached elements from the specified cache.
*
* #return the view.
*/
#RequestMapping(value = CACHE_REMOVE, method = RequestMethod.POST)
public String removeCachedElement(ModelMap model, #Valid #ModelAttribute(FORM_NAME) RemoveCacheElementForm form) {
model.addAttribute("removeElementResult", CacheUtils.removeCachedElement(form.getName(), form.getKey()));
initializeModel(model);
return CACHE_ADMIN_PAGE;
}
When I remove #Valid annotation, no worries too.
Anyone has an idea?
Thanks a lot for your help! :-)

Try changing your code to
#RequestMapping(value = CACHE_REMOVE, method = RequestMethod.POST)
public String removeCachedElement(ModelMap model, #Valid #ModelAttribute(FORM_NAME) RemoveCacheElementForm form, BindingResult bindingResult) {
model.addAttribute("removeElementResult", CacheUtils.removeCachedElement(form.getName(), form.getKey()));
initializeModel(model);
return CACHE_ADMIN_PAGE;
}

Related

Spring boot rest requestbody and #valid not working when object is null/empty

I am trying to apply not null validation on an attribute of my request which is instructedAmount but it is not working. I have a Spring Boot (V2.3.0.RELEASE) application with the following endpoints:
#Validated
public class TestController {
#PostMapping(value = "/test/pay")
public ResponseEntity<IPSPaymentResponse> validatePayt(#Valid #RequestBody InstantPaymentRequest instantPaymentRequest) {
log.debug("start validatePayment method {}", instantPaymentRequest);
....
The InstantPaymentRequest is as follows:
#Data
#Validated
public class InstantPaymentRequest {
#Valid
private PaymentIdentificationRequest paymentIdentification;
#NotBlank(message = "transactionTypeCode.required")
private String transactionTypeCode;
#Valid
private InstructedAmount instructedAmount;
#Valid
private CustomerRequest debtor;
The instructed amount is as follows:
#Data
public class InstructedAmount {
#NotBlank(message = "currency.required")
private String currency;
#NotBlank(message = "value.required")
private String value;
}
Basically when the instructedAmount is provided in the payload but for example I miss currency attribute in payload, the validation is working fine, the non null error message is displayed correctly and my rest controller endpoint is not called.
However when instructedAmount is not provided in the payload, no mandatory error message is displayed and my rest endpoint is called, it this the correct way or I am missing something?
I thought since attribute of InstructedAmount cannot be null then InstructedAmount also cannot be null/empty.
How to add InstructedAmount not null validation in the above scenario with annotation?
Use #NotNull together with #Valid:
#NotNull
#Valid
private InstructedAmount instructedAmount;
From https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-object-graph-validation:
Note that null values are getting ignored during cascaded validation.

Spring Validating requests params

I want to add validation annotations to my requests params in a Spring annotation. I have the required javax dependencies and code looks something like this
#RestController
public class Controller {
#RequestMapping(value = "/api", method = RequestMethod.GET)
public ExternalUserStatusCollection getUser(
#RequestParam(value = "userId", required = false) #Validated #Size(min = 5) #NotNull UserId userId,
{.....
}
However when I run the application, it doesnt validate the required fields. I dont want to modify the DTO of the object since the intention is to validate some DTO fields coming from external dependencies as well. How can I achieve this using annotations?
Annotating controller with #Validated leads to a bunch of other errors & doesnt help either

Stop some validations at the persist time in Spring

Is there a way to stop some validations to be executed at the time of data persist.
I know about
spring.jpa.properties.javax.persistence.validation.mode=none
but I believe that disables all validations, I just want to disable it for some of the fields (Password specifically, as it is encoded by that time and then pattern doesn't match). Thanks!
Update(More Details):
#Pattern(regexp = "^[A-Za-z0-9_#!]+$")
private String password;
I have a password field validated using the above pattern (A-Z, a-z, 0-9 and _,#,!). In the controller it validates success by below code.
#RequestMapping(value = "/adduser", method = RequestMethod.POST)
public ModelAndView signUp(ModelAndView modelAndView, #ModelAttribute #Valid LoginUser loginUser, BindingResult bindingResult) {
LoginUser loginUserAdded = null;
if (!bindingResult.hasErrors()) {
loginUser.setPassword(passwordEncoder.encode(loginUser.getPassword()));
loginUserAdded = createUser(loginUser);
....
But then before persist I encode the password and then it throws error while calling save method in JpaRepository because the password value has been changed by the encoder and it doesnt satisfy the pattern validation applied to it.
Now I am looking for a way by which I can disable validation on this field at the time of persist.

Custom form validation in Spring boot

I am working on a spring boot application and I have a password reset form. I am using a class like this to validate the inputs.
public class PasswordResetForm {
#NotEmpty
#Size(min=6, message="must be at least 6 characters")
private String password;
private String passwordConfirm;
//Getter and Setters
}
So, I now want to validate if the fields passwordConfirm and password are equals, I searched all over but could not find how to add a custom validation in this case. So, how do I add custom validation for other fields?
My controller's action looks like this
#RequestMapping(value = "/password-change/{id}-{tokenNumber}", method = RequestMethod.POST)
public String changePassword(#PathVariable String id, #PathVariable String tokenNumber, #Valid PasswordResetForm form, BindingResult formBinding, Model model) {
if (formBinding.hasErrors())
return "change-password";
//Other stuff
}
or if you wanna validate simply only this (passwordConfirm and password are equals) case.
you can use #AssertTrue.
#AssertTrue
public boolean isDifferentPass() {
return !password.equals(passwordConfirm);
}
if these two fileds are same , then your controller's BindingResult has error
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;
...
}
You can find additional reference for this here:
Dependency Injection in JSR-303 Constraint Validator with Spring fails
And on the Spring Docs:
http://docs.spring.io/autorepo/docs/spring/3.2.x/spring-framework-reference/html/validation.html
You can use #Validated annotation for forcing validation of #RequestParam and #PathVariable. #Valid is for forcing validation of #RequestBody

How can I reload my hibernate dependent objects before de validation

I have an Spring controller with code like:
#RequestMapping("save")
public String save(#ModelAttribute #Valid Form form, BindingResult result){
if( result.hasErrors()){
[...]
My form contains a list of hibernate objects. All have their properties setted. I create an edit HTML form and in the controller I find that all the objects on the ManyToOne relationships is lost. I only have the ID. I could reload data from the database but it is too late for the validation casued by the #valid annotation.
public class Form{
#Valid
#NotNull
private List<Item> item;
#NotNull
private Foo foo;
[...]
And Item
#Entity
#Table(name = "item")
#XmlRootElement
public class Item{
#ManyToOne()
#JoinColumn(name = "dependent", referencedColumnName = "id", nullable = false)
#NotNull
private Dependent dependent;
#NotNull
private Currency currency;
How could I set the Dependent and Currency fields before the validation? Is there any alternative to reload data from the database?
(Disclaimer some names have been changes to protect the inocent)
If you are using Spring-Data-JPA you can register DomainClassConverter to do this work for you. In another case you may write such converter by yourself.
I found one way to do it:
Add to the controller a reference to SmartValidator.
#Autowired private SmartValidator validator;
Remove the #valid annotation. Reload all ManyToOne tables and call manually the validator.
#RequestMapping("save")
public String save(#ModelAttribute Form form, BindingResult result){
for(Item item : form.getItems()){
item.setDependant( myDAO.reload(item.getDependent()));
}
validator.validate(form, result);
if( result.hasErrors()){
[...]

Resources