Validate Bean in Spring boot - spring

Is there a way in spring boot to validate properties in a bean? For Example, consider an Employee Bean consisting of following properties -
id - must start with 01,02,22
Department - Should be any of the one - D1, D2, D3
Name - Must not contain any digits and max length of 10 characters.
I can have a separate method and validate the bean every time but looking for some better way to implement this using spring boot.

You could use spring boot validation to validate your patterns.
Add this dependency to your gradle file implementation('org.springframework.boot:spring-boot-starter-validation') https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation checkout the latest version
class Employee {
#Pattern(regexp = "^(01|02|22).+$")
private String id;
#Size(max = 10)
#Pattern(regexp = "^[^0-9]+$")
private String name;
#Pattern(regexp = "^D[1-3]$")
private String department;
}
And in your request
#RestController
class EmployeeRequest {
#PostMapping("/registerEmployee")
ResponseEntity<String> registerEmployee(#Valid #RequestBody Employee employee) {
return ResponseEntity.ok("valid");
}
}
Note: I am not sure about syntax of regex but you should define your regex for your business requirements.

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.

Hi I'm trying to add #Value("${name}") private String name; in dto field but not able to read it from application.properties

class dto{#Value("${name}") private String name; }
application.properties
op.name=${OP_NAME:22-2}
//When I try to read it is returning null how to solve this
The #Value annotation will only be processed on Spring-managed beans (e.g. a #Component annotated class).
Additionally, you would need to specify the property key in #Value as follows in order to match with the key defined in your property file:
#Value("${op.name:fallback}")
private String name;
Your properties file can then carry the configurable value:
op.name=Some Name

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.

Ignoring spring mvc JSR-303 validations for selective entity fields

I have spring4 mvc application to save an Address entity, code bit as follows.
My Controller
#RequestMapping(value = "addAddress", method = POST)
public String registerComplaint(#Valid #ModelAttribute final Address address, final BindingResult resultBinder) {
if (resultBinder.hasErrors())
return "addAddress";
addressService.addAddress(address);
return "redirect:myAddress";
}
My Entity
#Entity
#Table(name = "address")
public class Address {
#NotNull
private String street;
#NotNull
private String pin;
#NotNull
private String createdBy;
........
}
My form conatins only street and pin as field, where as createdBy should be set by me after validating the other form values.
Here the problem is spring JSR303 validation support is validating a field ie createdBy which i don't want to validate by spring mvc.
How can i instruct spring mvc not to validate these kind of optional fields while using #Valid annotation.
Is there any way i can skip fields like this using spring mvc ?
Validation is mainly for user input. Since you will be setting createdBy yourself, just do so before saving it (e.g #PrePersist), or have a new Date as a default value. If you need to enforce a constraint for createBy, you can do so at the schema level.
#Column(nullable=false, ...)
private String createdBy = new Date();
You need to read up on Validation Groups. This lets you use different validators depending on the "scenario"
Use Spring's #Validated annotation to use groups
If you don't protect the createdBy field, a user can change it by altering the POST variables. See DataBinder.setDisallowedFields()
Conceptually, how is a pin related to an address?
It sounds like you want to use a Form Backing Object here (a regular non-JPA POJO made just for a form), and copy values to your real entities.

Resources