Spring MVC Based Rest Services Validations for request body - spring

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
*/
}

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?

JPA calling default constructor even during POST request

I didn't had a default constructor in my entity class in the beginning. Eventually found out that JPA requires a default constructor in entity class so I made one.
After adding the default constructor, even during post requests, JPA keeps calling default constructor which leads to incorrect initialisation of properties. For example, if you see the property called availableSeats, it is initialised to 100, but during post request only default constructor is called which leads to initialisation of availableSeats to 0.
This is extremely weird and I don't understand what am I doing wrong here.
#Entity
public class Flight {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotBlank(message = "Airline Name cannot be blank!")
private String airlineName;
#NotBlank(message = "From-Location cannot be blank!")
private String fromLocation;
#NotBlank(message = "To-Location cannot be blank!")
private String toLocation;
#NotBlank(message = "Airport Gate Number cannot be blank")
private String gateNumber;
// #NotBlank(message = "Boarding time cannot be blank")
private ZonedDateTime dateTimeZone;
private static final int INITIAL_SEAT_CAPACITY = 100;
private int availableSeats;
// constructor should not be able to set id
public Flight(Long id, String airlineName, String fromLocation, String toLocation, String gateNumber, ZonedDateTime dateTimeZone, int availableSeats) {
this.id = id;
this.airlineName = airlineName;
this.fromLocation = fromLocation;
this.toLocation = toLocation;
this.gateNumber = gateNumber;
this.dateTimeZone = dateTimeZone;
// setting up initial number of available seats
this.availableSeats = INITIAL_SEAT_CAPACITY;
}
public Flight(){
}
// getters and setters
}
Also adding FlightController.java code here
#RestController
#RequestMapping("/api/flights")
public class FlightController {
#Autowired
FlightService flightService;
#GetMapping(value = "/")
public ResponseEntity<List<Flight>> getAllFlights(){
return flightService.getAllFlights();
}
#PostMapping(value = "/")
public ResponseEntity<String> createFlight(#Valid #RequestBody Flight flight){
return flightService.createFlight(flight);
}
#GetMapping(value = "/{id}")
public ResponseEntity<Flight> getFlightById(#PathVariable Long id){
return flightService.getFlightById(id);
}
#DeleteMapping(value = "/{id}")
public ResponseEntity<String> deleteFlight(#PathVariable Long id){
return flightService.deleteFlight(id);
}
}
Spring's controller uses default(zero argument) constructor for object creation and then uses it's setter methods for setting the values in the object. You cannot expect for spring to use parameterized constructor.
So if you need to set some default values then do it in zero argument constructor.
As #grigouille pointed out in the comments, JPA only uses default constructor. Hence, availableSeats should have been initialised in the default constructor too.

Spring Boot validation of RequestBody Dto annotated in Rest API

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.

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.

Spring MVC with Hibernate Validator Mandatory for the database field, but not in the application

Problem with BindingResult hasErrors() in validation.
I have this code:
#RequestMapping(value = "/entity", params = "form", method = RequestMethod.POST)
public String submit(#Valid #ModelAttribute Entity entity, BindingResult result) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
entity.setCreatedBy(auth.getName());
if (result.hasErrors()) {
//Here the error of createdBy is null
return "entity/new";
} else {
entityService.save(entity);
return "redirect:/entity/list";
}
}
the entity class:
#Entity
#Table(name = "TABLE_X")
public class Entity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#NotNull
#Column(name = "primary_key")
private String primaryKey;
#NotNull
#Column(name = "created_by")
private String createdBy;
//getters and setter
}
I need set the value of createdBy in controller but always show "may not be null" in view.
Please help.
Spring MVC 4, Hibernate Validator 5, Database Oracle 11g
You entity object is validated before Spring MVC invokes the submit() method. The result object is created at the same time. This line:
entity.setCreatedBy(auth.getName());
has absolutely no effect on the outcome of result.hasErrors().

Resources