Spring Boot : How to do REST validation based on input group? - spring

I have a form in which :
firstname and lastname are mandatory fields for registered user.
ssn for new user.
contract number for owner.
So, on clicking the submit button, REST API (connect API) is called with values
from either of the above groups.
My bean class has members :
FN
LN
SSN
contractNum
How do I validate using bean/hibernate validator and identify which group has been passed ?

From the Hibernate Documentation, you can read for detail
https://hibernate.org/validator/
Hibernate Validator allows to express and validate application
constraints. The default metadata source are annotations, with the
ability to override and extend through the use of XML. It is not tied
to a specific application tier or programming model and is available
for both server and client application programming. But a simple
example says more than 1000 words:
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class User {
#NotNull
private String firstName;
#NotNull
private String lastName;
#NotNull
private String ssn;
}

Bean Validation is best used for simple validation logic. If your validation requires more complexity, use Spring's Validator interface instead.
I don't know the context domain, so I'll just call your bean "Form" with all String fields for the example:
public class Form {
private String firstName;
private String lastName;
private String ssn;
private String contractNumber;
// getters and setters
}
Then create a validator for this class:
public class FormValidator implements Validator {
public boolean supports(Class clazz) {
return Form.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
Form form = (Form) target;
// validation logic
}
}
Then you can simply use it like this:
Form form = ...;
Validator validator = new FormValidator();
Errors errors = new Errors();
validator.validate(form, errors);
if (errors.hasErrors() {
// not valid
} else {
// is valid
}

Related

How to validate DTO string field is one of specified values

My spring boot application has a DTO class for creating a task.
public class CreateTaskDTO {
#NotBlank
private String status;
}
I want the type field to be one of these values: "DONE", "IN_PROGRESS", "OPEN".
Is there hibernate constraints or any other workarounds I can validate thie field?
public enum Status() {
DONE, IN_PROGRESS, OPEN
}
public class CreateTaskDTO {
#NotNull
private Status status;
}
Enum must be created. The created enum should also be used in the DTO.

How to handle post and put request data validation

I have following user details model that is used in POST & PUT controllers of /user resource.
public class UserDetails {
#NotBlank
private String username;
#NotBlank
private String password;
#NotBlank
private String firstName;
#NotBlank
private String lastName;
#NotBlank
private String nic;
#NotNull
private Integer roleId;
// constructor & getters setters
}
#PostMapping("/org/employee")
public void createEmployee(#RequestBody EmployeeDetailsModel empDetails) {
employeeService.createUser(empDetails);
}
#PutMapping("/org/employee")
public void updateEmployee(#RequestBody EmployeeDetailsModel empDetails) {
employeeService.updateUser(empDetails);
}
Here, UserDetails has #NotNull & #NotBlank validations. POST would work fine because to create a user, all details are mandatory. But when updating with PUT, I don't need all properties of UserDetails to be filled.
So my questions are,
How this kind of scenarios are handled? Do we usually force clients to send all those details whether they are changed or not?
Is it possible to disable request body validation just for a particular endpoint or do I have to create separate model that looks the same but without validations?
Seeing your post I can infer that you are interested in modifying the resource
Well to do this you should to use PATCH method instead of PUT.
In PUT you need to send the entire data since it is intended for replacing the resource which is not in the case of the PATCH.
Well in case of the PUT or PATCH we need to ensure that we have an existing resource. Hence before saving it is necessary that we get the original resource from the data store. Then we can modify it with the help of the validation rules on the Entity itself.
so your code should be like.
Considering you have a repository class named as
EmployeeRepository
#PutMapping("/org/employee/{id}")
public void updateEmployee(#RequestBody EmployeeDetailsModel empDetails, #PathVariable("id") int id) {
Optional<Employee> emp = employeeRepo.findById(id);
if (emp.isPresent()) {
// update the new values using setters
// Finally update the resource.
employeeService.updateUser(empDetails);
} else {
throw new ResourceNotFoundException("Your custom msg");
}
}
The repository code should be placed inside the service method ie updateUser but I have placed it here just for demonstration.

Spring return selected field from domain

I've the following domain and needs to return selected field in response to client. How can I achieve that using Spring?
public class Vehicle {
private String vehicleId;
private Long dateCreated;
private String ownerId;
private String colourCode;
private String engineNumber;
private String transmission;
//getters & setters
}
My objective is to return only colourCode and transmission fields to client request. I've read about DTO and seems like I can achieve my objective with DTO but I don't find any good example how to implement it. Is DTO is the correct way to achieve my objective ?
Basically you just create VehicleDTO class with parameters you need
public class VehicleDTO {
private String colourCode;
private String transmission;
//getters and setters
}
and then in your code you construct VehicleDTO from your Vehicle class. Fortunately, we have BeansUtils class from Spring, that uses reflection to copy properties of one object to another, because you do not want to repeat logic for copying properties for every object. So it would be something like:
BeanUtils.copyProperties(v1, dto);
At the end your return VehicleDTO in your response instead of Vehicle
You can return IVehicle interface which exposes your properties of choice
public interface IVehicle {
String getTransmission();
String getColourCode();
}
and your Vehicle implents it
public class Vehicle implements IVehicle{ }
There are various ways you can achieve what you want.
You can add relevant usecase / APi specific DTO for the resource.
e.g. If your API return the vehical general details you may want to expose some level of details,
public class VehicleDetailsDTO {
private String colourCode;
private String transmission;
private String engineNumber; //more
//getters and setters
}
You can then either use BeanUtils or Dozzer to convert your Vehical resource to transportable object like your DTO.
BeanUtils : http://commons.apache.org/proper/commons-beanutils/
Dozzer : http://dozer.sourceforge.net/documentation/mappings.html
Assuming you use JSON as output format and Jackson as serialization engine (default in Spring MVC), you can tell Jackson to not serialize null properties. Now you just need to populate the properties you need and can return the original business object.

Custom bean validation not working

In my Student class I have many fields which I am storing in the database and I also have one field to store photo( for that I am using MultiPartFile datatype) and I am validating this field using custom validation.
Below is code for validation
#Component
public class PhotoValidator implements Validator{
#Override
public boolean supports(Class<?> clazz) {
return Student.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Student student=(Student)target;
if(student.getStudentPhoto()!=null){
if(student.getStudentPhoto().getSize()==0){
errors.rejectValue("file", "missing.file");
}
}
if(!student.getStudentPhoto().getOriginalFilename().endsWith(".jpg")){
errors.rejectValue("file", "invalid.file");
}
}
}
In the controller I am implementing it like this
#InitBinder
protected void initBinderStudent(WebDataBinder binder) { binder.setValidator(photoValidator);
}
My Student Model is :-
#Entity
#Table(name = "STUDENT")
public class Student extends UrlEntity {
#Transient
MultipartFile studentPhoto;
#Column(name = "COURSE_TYPE", nullable = false)
#NotNull(message = "Course Type: Course Type can not be left blank")
private in.jmi.constants.CourseType courseType;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "STUDENT_USER")
#Valid
private User user;
This custom validation of photo is not working and it also mess up the other annotation based validation that I am having here.
I have checked many posts in stackoverflow but couldn't find any relation to this particular problem.
Note:-If I remove the validation code from controller the code works just fine doing all the validations it is supposed to do.
You are mixing approaches in your example. You are not showing the imports in your code example, but the PhotoValidator class does not implement a Bean Validation constraints. It might be some Spring/JSF specific validator!?
To implement a Bean Validation constraint, you need to define a constraint annotation and at least one implementing ConstraintValidator. This is all described in Creating custom constraints. There are plenty of examples out there how to write a custom constraint.

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

Resources