Spring Boot validation of RequestBody Dto annotated in Rest API - spring

In my controller I have annotated the request parameter with the #Valid annotation and the field of my DTO with the #NotNull annotation, but the validation doesn't seem to work.
Are there any configurations to do in order to proceed with the validation? Following there are the Controller and the DTO class details.
#RepositoryRestController
#RequestMapping(value = "/download_pdf")
public class PurchaseController {
#Autowired
private IPurchaseService iPurchaseService;
#Loggable
#RequestMapping(value = "view_order", method = RequestMethod.POST)
public ResponseEntity getPDF(#RequestBody #Valid CustomerOfferDto offer,
HttpServletResponse response) {
return iPurchaseService.purchase(offer, response);
}
}
public class CustomerOfferDto {
#NotNull
private String agentCode;
// getter and setter...
}

Following are the steps I did to make it work.
Add dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Constraints in DTO class:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#ValidTaskDTO
public class TaskDTO {
#FutureOrPresent
#NotNull(message = "DueDate must not be null")
private ZonedDateTime dueDate;
#NotBlank(message = "Title cannot be null or blank")
private String title;
private String description;
#NotNull
private RecurrenceType recurrenceType;
#Future
#NotNull(message = "RepeatUntil date must not be null")
private ZonedDateTime repeatUntil;
}
RestController method with #Valid annotation on requestBody argument:
#RestController
#RequestMapping("/tasks")
#Validated
public class TaskController {
#PostMapping
public TaskDTO createTask(#Valid #RequestBody TaskDTO taskDTO) {
.....
}
}
On making a POST request with requestbody containing null value for dueDate, I got the expected error message as shown below.
{
"timestamp": "2021-01-20T11:38:53.043232",
"status": 400,
"error": "Bad Request",
"message": "DueDate must not be null"
}
I hope this helps. For details on class level constraints, hav a look at this video.

In my projects, this usually happens when I change my code from lets say Entity to DTO and forget to add #ModelAttribute to my DTO parameter.
If this also happened to you, try adding #ModelAttribute("offer") to your DTO parameter.

Related

SpringBoot + Thymeleaf - upload MultipartFile in form

I've done the following Thymeleaf form which takes some fields and a .pdf CV file
Form: link
There is the controller:
#Controller
#RequestMapping("/interview")
public class InterviewController {
#PostMapping(path = "/create", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
public #ResponseBody ResponseEntity<Object> createInterview(#RequestBody #ModelAttribute CreateInterviewTO createInterviewTO) {
ErrorRTO errorRTO = checkErrorsInsertInterview.validate(createInterviewTO);
//...Other
}
}
And at the end, DTO class:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class CreateInterviewTO {
private String site;
private String candidateName;
private String candidateSurname;
private Date candidateBirth;
private String mail;
private String eduQualification;
private String candidateType;
private String interviewType;
private String enterpriseId;
private MultipartFile curriculum;
}
When I send the request, I receive the following error:
w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]
UPDATE: when I sent request, curriculum DTO attribute is null.
Anyone has a solution?

Spring Boot List of Object Bean Validation

I have a Bean,
#Data
#NoArgsConstructor
public final class PersonRequest {
#NotNull
#JsonProperty("nameList")
private List<Person> nameList;
}
and Person POJO,
#Data
public class Sensor {
#NotNull
#JsonProperty("id")
private int id;
#NotNull
#JsonProperty("name")
#Min(1)
private String name;
}
I am sending JSON request and added #Valid in my controller. I am sending request as below,
{
"nameList": [
{
"id": 1,
"name": "John"
},
{
"id": 2,
"name": "Alex"
}
]
}
When i send request without id and name not validating. I tried using #Valid private List<Person> nameList; also but no luck. I use Spring boot 2.3.2.
UPDATED:
when i add one more attribute, this also say bad request when i pass date in request.
#NotNull
#JsonProperty("startTime")
#DateTimeFormat(pattern = "yyyy-MM-dd'T'hh:mm:ss", iso =
DateTimeFormat.ISO.DATE_TIME)
#Valid
private LocalDateTime startTime;
The #Valid annotation in your controller triggers the validation of the PersonRequest object, passed as request body. To validate also the Person objects contained in PersonRequest, you need to annotate that field with #Valid too.
#Data
#NoArgsConstructor
public final class PersonRequest {
#NotNull
#JsonProperty("nameList")
#Valid
private List<Person> nameList;
}

Spring 4.1.7 validate request body

I know this issue has been around there in other post, but after applying the fiux suggested was not working.
I am using spring 4.1.7 version, i want to validate the RequestBody from post rest call. For doing this i tried following set of codes, but it was not working as i expected.
My Request body pojo classes.
ParentPojo.class
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
#Validated
public class ParentPojo<T> implements Serializable{
#NotNull
private String cNumber;
#NotNull
private String statusCode;
#NotNull T child;
}
ChildPojo.class
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
#Validated
public class ChildPojo{
#NotNull
private String name;
#NotNull
private String address;
#NotNull
private String pin;
}
Controller:
Adding only methods
#Autowired
#Qualifier("validator")
private Validator validator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
#RequestMapping(produces = { "application/json", "application/xml" }, consumes ={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}, method = { RequestMethod.POST })
#ResponseStatus(HttpStatus.CREATED)
public Messageresponse<ChildPojo> create(#NotNull(groups = {ParentPojo.class, ChildPojo.class})
#Valid #Validated({ParentPojo.class, ChildPojo.class}) #RequestBody final ParentPojo<ChildPojo> ParentPojo, BindingResult bindingResult) {
System.out.println("new version 8="+bindingResult.hasErrors());
validator.validate(ParentPojo, bindingResult);
if(bindingResult.hasErrors()) {
System.out.println("Non formated form stuff.");
}
return service.create(ParentPojo);
}
#RequestMapping(value = "/{create}", produces = { "application/json", "application/xml" }, consumes ={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}, method = { RequestMethod.POST })
#ResponseStatus(HttpStatus.CREATED)
public Messageresponse<ChildPojo> create1(#NotNull #Valid #RequestBody final ParentPojo<ChildPojo> ParentPojo, BindingResult bindingResult) {
System.out.println("new version 8="+bindingResult.hasErrors());
validator.validate(ParentPojo, bindingResult);
if(bindingResult.hasErrors()) {
System.out.println("Non formated form stuff.");
}
return service.create(ParentPojo);
}
application context xml:
<mvc:annotation-driven validator="validator">
</mvc:annotation-driven>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
jar file tried:
hibernate-validator-4.3.0.Final
hibernate-validator-4.1.0.Final
hibernate-validator-4.0.2.GA
validation-api-1.1.0.Final
spring-context-4.1.7.RELEASE
But nothing was working with all the above combination for the request below:
in below request "pin" is missed and the controller method #Valid #RequestBody expected to handle this request as Bad Request. instead it is accepting the request body and processing further.
{
"cNumber" : "ff",
"statusCode" : "ddd",
"child" : {
"name" : "ll",
"address" : "ll"
}
Look at this question Here
You need to decorate the child pojo as #Valid
public class ParentPojo<T> implements Serializable{
#NotNull
private String cNumber;
#NotNull
private String statusCode;
#Valid
#NotNull
T child;
}

Spring MVC Based Rest Services Validations for request body

I have Rest Controller in my application which has the code snippet like below:-
#RestController
#RequestMapping("/api/v1/user")
public class UserRestControllerV1 {
#PostMapping("")
public Response registerUser(#RequestBody #Valid final Request<UserDto> request,
final HttpServletRequest httpServletRequest,
BindingResult result){
Response response = new Response(request);
if(result.hasErrors()){
response.setData(new String("Error"));
}else {
response.setData(new String("Test"));
}
return response;
}
The Request Class:-
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Request<T> {
#JsonProperty(value = "co-relation-id")
private String coRelationID;
#NotNull(message = "The request body should be present")
private T data;
/*
..... various other fields
Getters / Setters
*/
}
The UserDto Class :-
public class UserDto {
#NotNull(message = "The username should not be null")
private String username;
#NotNull(message = "The password should not be null")
#JsonIgnore
private String password;
/*
..... various other fields
Getters / Setters
*/
}
Issue : I am having issues with my validations here. The field private T data in the request class gets validated but the fields inside T - in the case UserDto are not getting validated.
So I need to know the approach or code snippet to achieve this.
I have tried configuring the hibernate validator bean in the configuration but it is of no help in the scenario
#Valid constraint will instruct the Bean Validator to delve to the type of its applied property and validate all constraints found there.
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Request<T> {
#JsonProperty(value = "co-relation-id")
private String coRelationID;
//#NotNull(message = "The request body should be present")
#Valid
private T data;
/*
..... various other fields
Getters / Setters
*/
}

Spring Request Mapping post vs put, same method, same logic, but

I have a 2 method:
first one create product:
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> create(#Validated ProductDTO productDTO){
productService.addProduct(productDTO);
return new ResponseEntity<>("Maxsulot ro'yhatga qo'shildi", HttpStatus.OK);
}
another one update product:
#RequestMapping(method = RequestMethod.PUT)
public ResponseEntity<?> update(#Validated ProductDTO productDTO){
productService.update(productDTO);
return new ResponseEntity<>("Maxsulot ma'lumotlari yangilandi", HttpStatus.OK);
}
Now, I am surprized that, if I sent same data post method works fine(screen1), but put(screen2) method return validation error.
screen1(post)
screen2(put)
What the problem is?
MyDTO class:
public class ProductDTO {
private Long id;
private MultipartFile file;
#NotNull
#Size(min = 2, max = 50)
private String productName;
#NotNull
private Long productPrice;
private String productInfo;
#NotNull
private Long categoryId;
private String unitOfMeasurement;
// getters and setters
}
I can see you have #Validated that should validate your request body according to JSR-303.
Seems like it is not consistent when you POST and PUT. It validates/not validating and return an error because your body does not match the validation rules you placed on ProductDTO.
In all the docs I saw you should do something like #Valid #RequestBody instead of just putting #Validated.
Try to change it to the above and see if it now work more consistently.

Resources