Change #RequestBody object based on #PathVariable - spring

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.

Related

Creating custom requestParam in springboot controller

i have a use case where the user can send following params with get request
GET localhost/8080/users?filter=alex
OR
GET localhost/8080/users?suffixFilter=lex
OR
GET localhost/8080/users?prefixFilter=a
is it possible to add only one request param instead of 3 in controller
e.g.
instead of
#GetMapping("/users")
#ResponseBody
public String getFoos(#RequestParam String filter, #RequestParam String prefixFilter , #RequestParam String suffixFilter) {
return "ID: " + id;
}
is it possible to create a dynamic one which includes those three variantes? I was thinking creating class like this
public class FilterCreteria {
private String filter;
private String suffixFilter;
private String prefixFilter;
}
and then passing this to controller
#GetMapping("/users")
#ResponseBody
public String getFoos(#RequestParam FilterCreateria filter) {
return "ID: " + id;
}
the problem is if i send filter=alex the other attributes in the class FilterCreteria is null and i want to avoid this null.
What i searching for:
is there other way of making this possible?
if my suggestion is ok, how to avoid null attributes when the user only sends one queryparam like filter=alex?

Spring boot rest requestbody and #valid not working when object is null/empty

I am trying to apply not null validation on an attribute of my request which is instructedAmount but it is not working. I have a Spring Boot (V2.3.0.RELEASE) application with the following endpoints:
#Validated
public class TestController {
#PostMapping(value = "/test/pay")
public ResponseEntity<IPSPaymentResponse> validatePayt(#Valid #RequestBody InstantPaymentRequest instantPaymentRequest) {
log.debug("start validatePayment method {}", instantPaymentRequest);
....
The InstantPaymentRequest is as follows:
#Data
#Validated
public class InstantPaymentRequest {
#Valid
private PaymentIdentificationRequest paymentIdentification;
#NotBlank(message = "transactionTypeCode.required")
private String transactionTypeCode;
#Valid
private InstructedAmount instructedAmount;
#Valid
private CustomerRequest debtor;
The instructed amount is as follows:
#Data
public class InstructedAmount {
#NotBlank(message = "currency.required")
private String currency;
#NotBlank(message = "value.required")
private String value;
}
Basically when the instructedAmount is provided in the payload but for example I miss currency attribute in payload, the validation is working fine, the non null error message is displayed correctly and my rest controller endpoint is not called.
However when instructedAmount is not provided in the payload, no mandatory error message is displayed and my rest endpoint is called, it this the correct way or I am missing something?
I thought since attribute of InstructedAmount cannot be null then InstructedAmount also cannot be null/empty.
How to add InstructedAmount not null validation in the above scenario with annotation?
Use #NotNull together with #Valid:
#NotNull
#Valid
private InstructedAmount instructedAmount;
From https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-object-graph-validation:
Note that null values are getting ignored during cascaded validation.

OpenApi add example for request body

Am working with Spring boot and I am using springdoc-openapi-ui to generate spec files using swagger editor
The issue Is, Am trying to avoid creating model classes just to use them in the request body to show them with swagger UI.
For example :
#RequestMapping(value = "/update/project/{id}", method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> projectUpdate(#RequestBody ObjectNode json, #PathVariable int id)
{ ... }
If I use it like this, the example will be empty on the Swagger UI page.
So as a solution, I have to do it like the following
public class CustomerCreateRequest {
#JsonProperty
private String ProjectId;
#JsonProperty
private String ProjectName;
#JsonProperty
private String ProjectDescription;
#JsonProperty
private String CustomerId;
#JsonProperty
private String CustomerName;
#JsonProperty
private String CustomerDescription;
#JsonProperty
private String BucketId;
#JsonProperty
private String API_KEY;
#JsonProperty
private String Name;
#JsonProperty
private String RedmineId;
And then I can use the model class I just created like the following.
#PostMapping(value = "/createUser")
public ResponseEntity createCustomer(#RequestBody CustomerCreateRequest requestBody)
{ ... }
Question
Is it ok to do a model class just for this purpose?
Is there a way to add an example so the UI team will have an idea of how to use it.
I know that a model class can be helpful in generating a client for UI ( like JSClient ) But is it really necessary? I mean can't we overcome this issue?
Any Answer, Suggestion, Links are appreciated, the swagger docs was not helpful in my case.
my two cents:
Is it ok to do a model class just for this purpose?
Yes, you should use a model class for your #RequestBody becouse every endpoint must have a contract to communicate the payload necessary to be consumed.
It's a good practice add the annotations like
#Parameter(description="Some description", example="{\"foo\":\"bar\"}")
#RequestBody CustomerCreateRequest requestBody
Is there a way to add an example so the UI team will have an idea of how to use it.
No, Swagger will map a POJO class with decorators such as #Schema and others. ObjectNode has not a valid representation for the use case
I know that a model class can be helpful in generating a client for UI ( like JSClient ) But is it really necessary? I mean can't we overcome this issue?
Well, in my experience use tools as Swagger have more benefits than cons. It's necessary take care about the constraints related? I think so

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.

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

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){
...
}

Resources