Marking a field as mandatory in the request body for a specific request - spring

I intend to use the same bean as request body for creating/updating/deleting profile (depending on request methods). There are two scenarios for an update. First is updating profile where any parameter like firstName, lastName etc. can be updated except the emailAddress and the second is updating only the emailAddress. For the first case newEmailAddress is optional but for the second case, newEmailAddress field is mandatory.
Below is a sample bean:
class ProfileModel {
#NotEmpty
#Email
private String emailAddress;
#Size(max=30)
private String firstName;
#Email
private String newEmailAddress;
.
.
.
}
I created different API endpoints for updating profile and updating the email address. I tried to find a way to make newEmailAddress field as optional for the first request and required for the second request but couldn't find anything but to manually check in the controller method for the second request and throw Exception.
Is there any other way through which this can be achieved?

The validation groups should solve your problem. See the examples either on beanvalidation.org or at hibernate-validator documentation page.
Basically you will need to add a group attribute value to your annotation constraints. Something like:
class ProfileModel {
#NotEmpty
#Email
private String emailAddress;
#Size(max=30)
private String firstName;
#Email(groups= UpdateEmail.class)
private String newEmailAddress;
and then on your controllers make use of Springs #Validated annotation which allows you to pass a group for which you want to validate.
One endpoint could use then this UpdateEmail group and the other the Default one.

This can be achieved using validation groups. That needs an identifier class or interface.
So you can do something like this:
=> Define a validation group identifier. It can be a class or interface.
public interface MyValidationGroup{}
=> Specify the validation group on request body.
class ProfileModel {
#NotEmpty
#Email
private String emailAddress;
#Size(max=30)
private String firstName;
#Email
#NotBlank(groups={MyValidationGroup.class})
private String newEmailAddress;
.
.
.
}
=> Specify validation group on controller method.
public ProfileModel add(#Validated({MyValidationGroup.class})
#RequestBody ProfileModel profile){
...
}

Related

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.

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.

Change #RequestBody object based on #PathVariable

Below is my application code
#RequestMapping(value = "app/{version}/register", method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public RegisterMemberResponse registerMember(#Valid #RequestBody RegisterMemberRequest request,#PathVariable Integer version){
return registerservice.register(request);
}
for app version 1 my RegisterMemberRequest class is
public class RegisterMemberRequest{
#NotNull
private String firstName;
#NotNull
private String lastName;
//gatter & setter...
}
and for app version 2 my request class is
public class LatestRegisterMemberRequest extends RegisterMemberRequest{
#NotNull
private String middleName;
//getter & setter...
}
so how i can i change my registerMember() method in such a way that i can serve both version 1 & 2 request uri.
If you want to hardcode the version number into the url (which I would not recommend), you could easily use two different controller methods with hardcoded paths like app/v1/register and app/v2/register.
Another way would be to use content negotiation and route the requests according to a given content-type, e.g. application/vnd+<YOURCOMPANY>.<DATATYPE_AND_VERSION>+json, also using separate rest controller methods annotated with #Consumes.
I think you can make use of Requestmapping-uri-templates-regex , version number will be the #PathVariable write your business logic to decide what should be the response body depends on pathvariable version.

Spring: How do I construct this command object through a GET HTTP request?

I'm using Spring 3.1.0.RELEASE with Hibernate 4.0.1.Final. I want to invoke a search method in a controller that takes as input a search bean (the Event bean below) ...
#RequestMapping(value = "/search_results.jsp")
public ModelAndView processSearch(final HttpServletRequest request, final Event searchBean, final BindingResult result) {
...
}
The event bean contains the following field ...
#Entity
#Table(name = "EVENTS")
public class Event implements Comparable {
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="EVENT_FEED_ID")
private EventFeed eventFeed;
...
}
in which the EventFeed object contains the following fields ...
#Entity
#Table(name = "EVENT_FEEDS")
public class EventFeed {
#Id
#Column(name = "ID")
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
#NotEmpty
#Column(name = "TITLE")
private String title;
...
}
How do I construct a URL such that the search bean's Event.getEventFeed().getId() field is populated?
I realize I could submit a GET request with a parameter like "eventFeedId=2" and populate everything manually, but since other pages are submitting requests that populate the command object, I'd like to continue to use the same logic.
It would be
/search_results.jsp?event.eventFeed.id=...&event.eventFeed.title=...
event is a default model attribute name as defined in #ModelAttribute, other binding rules are described in 5.4.1 Setting and getting basic and nested properties.
Note, however, that this approach can cause problems if you'll associate these bean with Hibernate session later. For example, if you want to attach new Event to the existing EventFeed by calling merge() it would also override the title property. Thus, in such a case it would be better to avoid overuse of data binding and pass primitives as parameters instead.

Spring: Same object, different validation

I have an object called User where I save all the data of the User. I have some annotations to perform validation and it works fine.
public class User{
#NotEmpty
#Email
#Size(max=100)
#Column(name="username", length=100, nullable=false, unique=true)
private String username;
#NotEmpty
#Size(min=5, max=40)
#Column(name="password", length=40, nullable=false)
private String password;
#Size(min=5, max=40)
#Transient
private String newPassword;
// other attributes ,getters and setters
}
I have two different forms (each one in a different page). In the first one I ask for username and password to create the user, so both of them are compulsory.
In the second form I show the information about the user: username, other data (which will be validated as well) andthe password and newPassword. If the newPassword is set and the password is the same as the user has I'll change the user's password, but if they are left empty it means I shouldn't change the password.
The problem is that I have two forms relating to the same object where there is a different validation for the field password. In the first one it must be not empty, but in the second one it can be empty.
In the controller I validate the object in this way:
public String getUserDetails(#Valid #ModelAttribute("User") User user, BindingResult result, Model model){
if(result.hasErrors()){
//There have been errors
}
...
}
but is password is empty there will be an error.
Is there any way to perform a validation only in some fields of the object?
Can I, at least, remove any validation error after the validation?
What is the best practice in this case?
Thanks
You can use JSR-303 constraint groups to achieve this.
public class User {
public interface GroupNewUser {};
#NotEmpty
private String username;
#NotEmpty(groups = {GroupNewUser.class});
private String password;
// ...
}
Then in the controller, instead of using #Valid, use Spring's #Validated annotation which allows specifying a constraint group to apply.
See this blog post and this one also for more information.
There is also this superb post for Spring usage.
This is the problem with declarative validation, it's not very easy to do this sort of thing.
The easiest solution is to remove the validation annotations from the password field, and validate that field manually in the controller. The BindingResult class has methods on it for you to explicitly mark fields as invalid.
Another alternative would be to create two subclasses of the form class, each with its own password field, one with validation annotations, and one without, and use the appropriate one in the appropriate place.
A few pointers. I'm not sure they will be useful, though:
Spring docs say that:
you may call binder.setValidator(Validator) within a #Controller's #InitBinder callback. This allows you to configure a Validator instance per #Controller class
javax.validation has two *Context interfaces. Get into more details with them to see whether different validation can be achieved in different contexts.
instead of using hasErrors() function, use hasFieldErrors(fieldname) and only validate particular fields required by the form.

Resources