Spring #RequestBody validation issue using #Valid annotation - spring-boot

I am trying to perform validation on Spring Boot #RequestBody JSON object using #Valid annotation.
This JSON object POJO class will contain another nested object.
I have added field-level annotation for the nested object classes.
Also, I have added the #Valid annotation when adding the nested class object in the main object class.
But still, the validation for the nested class objects is not getting triggered when we are not passing the correct object.
Please find the code below for reference.
ExceptionController.class
#RestController
#RequestMapping("/student")
public class ExceptionController {
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public String createStudent(#Valid #RequestBody Student student, HttpStatus status) {
Student student1 = student;
return "student data created!!";
}
}
Student.class
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Student {
#NotEmpty(message = "First Name field can not be null or empty")
#NotBlank(message = "First Name field can not be blank")
private String firstName;
#NotEmpty(message = "Last Name field can not be null or empty")
#NotBlank(message = "Last Name field can not be blank")
private String lastName;
#Valid
private Subject subject;
}
Subject.class
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Subject {
#NotEmpty(message = "Topic field can not be null or empty")
#NotBlank(message = "Topic field can not be blank")
private String topic;
}
When I am not sending any data for Subject in the request it is not giving any exception and returns HTTP 200 OK status.
Any help will be highly appreciated.

Try adding #NotNull annotation to Subject subject:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Student {
#NotEmpty(message = "First Name field can not be null or empty")
#NotBlank(message = "First Name field can not be blank")
private String firstName;
#NotEmpty(message = "Last Name field can not be null or empty")
#NotBlank(message = "Last Name field can not be blank")
private String lastName;
#Valid
#NotNull
private Subject subject;
}
Your current validations would only work if you actually send Subject data but with an empty topic attribute.
On another topic, you might drop the #NotEmpty annotations because #NotBlank does not allow null or empty Strings.

Related

How do I link two entities within a #RestController?

I have the following endpoint:
#PostMapping
Employee createEmployee(#Valid #RequestBody Employee newEmployee, BindingResult bindingResult) {
return employeeRepository.save(newEmployee);
}
I can send POST requests as JSON and have an employee created, but how do I link associated entities, such as a Department entity?
When I send a post request, it looks like this (I know it's not valid JSON, it's just browser console output):
departmentId: 1
emailAddress: "dillon#james.com"
firstName: "Dillon"
lastName: "James"
phoneNumber: undefined
The department exists in my database, and has an ID of 1. I thought that Spring would automatically set the departmentId somehow on my Employee model, but it doesn't seem to do that, or I must be missing something.
I'm basically trying to set the department of the newly created employee, but how I have it currently doesn't seem to do so.
For reference, this is how my Employee entity is defined:
#Entity
#Getter
#Setter
public class Employee {
#Id
#GeneratedValue
private Long id;
#NotNull
private String firstName;
#NotNull
private String lastName;
#NotNull
#Column(unique = true)
private String emailAddress;
#ManyToOne
private Department department;
private String phoneNumber;
}

Form validation in thymeleaf by Springboot

I am new to the Springboot and Thymeleaf, I am creating a web where i am using form validation where I am trying to validate fields from an entity class to validate non empty fields.
import javax.validation.constraints.NotBlank;
public class Student {
#NotBlank(message = "Name required")
public String name;
#NotBlank(message = "email required")
public String email;
#NotBlank(message = "address required")
public String address;
#NotBlank(message = "username required")
public String username;
#NotBlank(message = "password required")
public String password;
'''
constructor
getter and setter
'''
}
It is not showing error in my html file, showing error on server.
If any one has any idea please help me.
In order this to work add #valid annotation next to #ModelAttribute("student").
public String addUser(#Valid #ModelAttribute("student") Student student, BindingResult result, Model model){
And also try adding #NotEmpty above the field of Student entity. #NotEmpty will check if the object has empty strings.
Try this,
public String addUser(#Valid #ModelAttribute("student") Student student, Model model){
...
}
Also, add #NotNull on entity attributes and check once.
Try out this:
public String addUser(#Valid #ModelAttribute("student") Student student, BindingResult result, Model model){
Also, add #NotNull on entity attributes and check once.

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

Got status 500 and message "Missing URI template variable 'rank'", whenever test HTTP POST request on postman

I got the error "Resolved [org.springframework.web.bind.MissingPathVariableException: Missing URI template variable 'rank' for method parameter of type Rank]" on eclipse console
And message: "Missing URI template variable 'rank' for method parameter of type Rank" with status "500" whenever try HTTP POST request
My RESTController code:
#RestController
#RequestMapping(path = "/comp")
public class RankController {
#PostMapping(path = "/rank")
ResponseEntity<Rank> createRank(#Valid #PathVariable Rank rank) throws URISyntaxException{
Rank result = rankRepository.save(rank);
return ResponseEntity.created(new URI("/comp/rank" + result.getId())).body(result);
}
}
My Rank entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "RANK_TBL")
public class Rank {
#Id
private Long id;
private String name;
#ManyToOne(cascade = CascadeType.PERSIST)
private Employee employee;
}
My Employee entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "EMPLOYEE_TBL")
public class Employee {
#Id
private Long id;
private String name;
private String email;
#OneToMany
private Set<Rank> Rank;
}
Change #PathVariable with #RequestBody
Here you are making a request to save the entity and you should pass the payload as #RequestBody in JSON format. From postman you may use raw type and select the JSON type.
Ideal way is to use #RequestBody whenever we are creating or updating the records which requires the object to passed with POST and PUT methods. For methods which retrieves the records based on Id or some parameters you may use #PathVariable
You may explore more about the annotations here

converting URI to entity with custom controller in spring data rest?

i have an jpa entity like this.
#Entity
#Table(name = "location")
#Data
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "LOCATION_ID", unique = true)
#NotEmpty(message = "Please Enter Location ID")
private String name;
#Column(name = "LOCATION_DESCRIPTION")
#NotEmpty(message = "Please Enter Location Description")
private String description;
#ManyToOne
#NotNull(message = "Please Choose a Building")
Building building;
#Version
Long version;
}
and the repository like this.
public interface LocationRepository extends PagingAndSortingRepository<Location, Long> {
Location findByName(#Param("name") String name);
}
i am using spring data rest i am able to create location with rest api by providing the following payload
{
"name":"adminxxxxx","description":"adminxxx" , "building": "http://localhost:8080/buildings/2"
}
now i am trying to write my custom controller which will persist the entity. this is my custom controller
#ExposesResourceFor(Location.class)
#RepositoryRestController
#BasePathAwareController
public class LocationController {
#Autowired
LocationRepository locationDao;
#Autowired
LocationResourceAssembler resourceAssembler;
#Value("${buildings.error.messages.uniqueconstraintviolation}")
String uniqueConstrainMessage;
static final String TAG = LocationController.class.getSimpleName();
#RequestMapping(value="locations",method = org.springframework.web.bind.annotation.RequestMethod.POST)
public ResponseEntity<?> save(#RequestBody #Valid Location location) {
try {
location = locationDao.save(location);
LocationResource b = resourceAssembler.toResource(location);
return ResponseEntity.ok().body(b);
} catch (DataIntegrityViolationException e) {
if (locationAlreadyExists(location.getName()))
throw new LocationAlreadyExistException(uniqueConstrainMessage, location);
else
throw new RuntimeException("Some Error Occured");
}
}
i am getting this error
exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.alamdar.model.Building: no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/buildings/2')
at [Source: java.io.PushbackInputStream#5d468b16; line: 3, column: 60] (through reference chain: com.alamdar.model.Location["building"])</div></body></html>
can anyone please help?
I am not sure why you are writing a custom controller however the issue would appear to be that you do not have a default no args constructor so Jackson cannot instantiate an instance.
This is because you are using Lombok's #Data annotation:
https://projectlombok.org/features/Data.html
You should also annotate you class with #NoArgsConstructor to have a default no-args constructor generated:
#Entity
#Table(name = "location")
#Data
#NoArgsConstructor
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "LOCATION_ID", unique = true)
#NotEmpty(message = "Please Enter Location ID")
private String name;
#Column(name = "LOCATION_DESCRIPTION")
#NotEmpty(message = "Please Enter Location Description")
private String description;
#ManyToOne
#NotNull(message = "Please Choose a Building")
Building building;
#Version
Long version;
}

Resources