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

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.

Related

javax-validation annotation is not working for member variables that is declared as another ObjectType

I have added annotations in the parent class.
It is working fine.
But it is not working in the member variables that is declared as another Object type. It is validating:
orderId from base class
referenceNumber from MarchantApplicationRequest
#NotEmpty annotation at customerRequests field in MerchantApplicationRequest.
But it is not validating customerRoleType in CustomerRequest.
Also, I would like to add #NotBlank annotation in customerRequests. But it is not taking this, though it is taking #NotEmpty annotation.
Class MerchantApplicationRequest
#JsonIngnoreProperties(ignoreUnknown=false)
public class MerchantApplicationRequest extends IomBaseDTO {
#NotEmpty(message="customerRequests is mandatory")
private List<CustomerRequest> customerRequests;
#NotBlank(message="referenceNumber is mandatory")
private String referenceNumber ;
}
Class CustomerRequest
public class CustomerRequest {
#NotBlank(message="customerRoleType is mandatory")
private String customerRoleType ;
}
Controller class
Method where to apply validation:
#PostMapping("/orderDetail")
public void orderDetail(#Valid #RequestBody MerchantApplicationRequest request) {
try {
iOrderService.updateProductDetail(request);
} catch (Exception e) {
// ...
}
}
Here is my JSON payload:
{
"orderId" : 101,
"referenceNumber" : "123",
"customerRequests" : [ {
"customerRoleType" : null
}]
}
I am using in pom.xml of Spring Boot application:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
If you want to cascade the validation you have to add the #Valid annotation:
#Valid
#NotEmpty(message="customerRequests is mandatory")
private List<CustomerRequest> customerRequests;
Please read more about cascading in the Hibernate Validation documentation: Example 2.11: Cascaded validation
Using bean-validation (javax.validation), you can add validation to elements of collections.
Using Bean-Validation 1.0
#JsonIngnoreProperties(ignoreUnknown=false)
public class MerchantApplicationRequest extends IomBaseDTO {
#NotEmpty(message="customerRequests is mandatory")
#Valid
private List<CustomerRequest> customerRequests;
#NotBlank(message="referenceNumber is mandatory")
private String referenceNumber ;
}
See also:
JSR 303: How to Validate a Collection of annotated objects?
LogicBig Tutorial: Collection Validation
Alternative since Bean-Validation 2.0
In Java 8 generic types can also be validated by prepending the annotation before the type inside the diamond-operator, e.g. <#Valid CustomerRequest>. This is a more concise way of defining per-element validation. It has the same effect like the traditional way, validates every given element as defined in the class (CustomerRequest).
See also:
java/beans validation - collection/map does not contain nulls
Baeldung Tutorial: Validating Container Elements with Bean Validation 2.0

Throw error when properties marked with #JsonIgnore are passed

I have a requirement to mark certain properties in my REST beans as ignored using #JsonIgnore. (I am using Spring Boot). This helps in avoiding these properties in my Swagger REST documentation.
I also would like to ensure that if the client passes these properties, an error is sent back. I tried setting spring.jackson.deserialization.fail-on-unknown-properties=true, but that works only for properties that are truly unknown. The properties marked with #JsonIgnore passes through this check.
Is there any way to achieve this?
I think I found a solution -
If I add #JsonProperty(access = Access.READ_ONLY) to the field that is marked as #JsonIgnore, I get back a validation error. (I have also marked the property with #Null annotation. Here is the complete solution:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
#Null(message = "Id must not be passed in request")
private String id;
private String name;
//getters and setters
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeRequest extends Employee {
#Override
#JsonIgnore
#JsonProperty(access = Access.READ_ONLY)
public void setId(String id) {
super.setId(id);
}
}
PS: By adding #JsonProperty(access = Access.READ_ONLY), the property started showing up in Swagger model I had to add #ApiModelProperty(hidden = true) to hide it again.
The create method takes EmployeeRequest as input (deserialization), and the get method returns Employee as response (serialization). If I pass id in create request, with the above solution, it gives me back a ConstraintViolation.
PS PS: Bummer. None of these solutions worked end-to-end. I ended up creating separate request and response beans - with no hierarchical relationship between them.

#Valid for long data type is not working for mandatory check

I have the below input class and when i trigger the api without 'interactionId' param in the input,
I expect validation error message "interactionId cannot be empty" but the validation passes through which i guess could be due to the fact that interactionId has a default value of 0.
Can someone pls. help to enforce this validation on the 'long' parameter when its not given in input?
with #NotEmpty for the customerId param, its working as expected. Using #NotEmpty for the long param "interactionId" is throwing a different error that #notempty cannot be used for long.
public class Input {
#NotEmpty(message = "customerId cannot be empty")
private String customerId;
#Valid
#NotNull(message = "interactionId cannot be empty")
private long interactionId;
// setters and getters
}
my controller class:
#RestController
public class Controller {
#PostMapping(value="/detailed-customer-transaction", produces =
MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<Object> detailTransactions(#Valid #RequestBody Input
params)
{
return new ResponseEntity<>(Dao.detailTransactions(params), HttpStatus.OK);
}
Above issues is resolved after changing to Long instead of long.
Query #2
I need another help. I have a String input param which takes date-time format in below format. Given its a string parameter, how can i validate for the pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
long should be Long, because long is a primary type in java, not an object, so Long is an object that can be checked whether it is null

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

400 Bad request with Hibernate #Valid

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;
}

Resources