Wrapper type Path variable won't accept null value - Spring Boot - spring-boot

This is our Controller
#GetMapping(path = "/getUsersForHierarchy/{entity}/{hierId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<UserInfo>> getUsersForHierarchy(#PathVariable(name = "entity", required = true) int entity,
#PathVariable(name = "hierId", required = true) Integer hierId) {
..................
..................
}
We are expecting two Path variables, one is of int type (entity) and the other is Integer type (hierId). hierId can be null or any numeric value and thats why its kept as Wrapper. But its gives error while firing the request like this
http://localhost:5010/v1/accountholder/userinfo/getUsersForHierarchy/5/null
Now a Wrapper is meant to accept null values, but we are getting error like this
Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: "null"
If we change Integer to String the call is getting inside the controller, but for further proceedings we need to use Integer.
We looked into this Spring MVC #Path variable with null value, and tried invoking the API with change in URL like this
http://localhost:5010/v1/accountholder/userinfo/getUsersForHierarchy/5/blaaa/null, but the error is still the same.
What should be done?

If you want it to be nullable you can achieve it with the following. First of all, if it's nullable the required property should be false. Also, considering required = true is the default, there's no need to specify it and if the name of the path variable matches the name of the corresponding variable you don't have to specify the name either.
#GetMapping(value = {
"/getUsersForHierarchy/{entity}/",
"/getUsersForHierarchy/{entity}/{hierId}"
},
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<UserInfo>> getUsersForHierarchy(
#PathVariable int entity,
#PathVariable(required = false) Integer hierId) {
}
Considering I don't like to deliberately allow a null value into the application, another nice to have could be having the hierId required with type Optional, so the following will give you a Optional.empty when just /getUsersForHierarchy/123/ is invoked and hierId is null. Otherwise it will populate the optional when hierId is provided invoking /getUsersForHierarchy/123/321:
#GetMapping(value = {
"/getUsersForHierarchy/{entity}/",
"/getUsersForHierarchy/{entity}/{hierId}"
},
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<UserInfo>> getUsersForHierarchy(
#PathVariable int entity,
#PathVariable Optional<Integer> hierId) {
}

Related

Swagger: How to use password format (Parameter annotation) for a String field ? Currently it won't allow me to submit the form with a value

I have a Spring Boot app (2.7.0) with spring docs (1.6.9)
Endpoint defined as follows:
#PostMapping(value = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
#Operation(summary = "blah blah blah")
public ResponseEntity<UploadSummary> uploadData(
#RequestParam("file") final MultipartFile csvFile,
#Parameter(schema = #Schema(type = "string", format = "password"))
#RequestParam("password") final String password,
#RequestParam(name = "dryRun", required = false, defaultValue = "true") final boolean dryRun) {
I want to have the password input field on the Swagger interface mask the input. The only change I have made above is to include a single #Parameter annotation, nothing else.
The Swagger interface does not appear to like this single Parameter annotation at all, regardless of format. If I input a value in the password field, it is always highlighted with a red border and I cannot execute.
I tried upgrading to 1.6.12 but still doesn't work.
Perhaps other annotations or code needs to be setup before these Parameter annotations and Schema types work ?

how to validate size of pathvariable that is string type to be exact value in spring boot?

How I can validate size of path variable to be exact size? Variable is string type.
#GetMapping("getLegal/{legalCode}")
public ResponseEntity getLegalFrom(
#PathVariable #Size(min = 9,max = 9) String legalCode) {
return sharedService.getLegal(legalCode);
}
You can use #PathVariable annotation with Regular Expressions. To do that Change your #GetMapping to
#GetMapping("getLegal/{legalCode:[a-zA-Z0-9]{9,9}}}")

The API handles a faulty request without raising an error eventhough annotated with ApiModelProperty with dataType

I am getting the following error - "The generated value is of the type integer instead of the type ‘string’" with 42crunch conformance scan with the following problem reported - "The API handles a faulty request without raising an error"
In the test scan, it is trying to pass the following request body -
{"type":1,"visitId":"xxxx","visitorId":"Ag5w\u0010l\u0014"}
So in above request, type is passed as integer but API is declared with type with "String" property.
#ApiModelProperty(dataType = "java.lang.String", value = "Type", example = "Data")
#Pattern(regexp = "^.*$")
#Size(min = 1, max = 50)
private String type;
I have the above annotation for the input request class. So eventhough type property is declared with ApiModelProperty annotation with dataType accepting only "java.lang.String", why is it throwing error?
I also have the #Valid annotation for the controller endpoint method which is accepting the request body.
public ResponseEnvelope recommendedProducts(
#Valid #RequestBody RecommendedProductRequest productRequest,
HttpServletResponse httpServletResponse) {
Please let know how to resolve this. Thanks!

How to collect all fields annotated with #RequestParam into one object

I would like to gather all of my query parameters into a pojo and perform additional validation of the fields.
I have read that I can simply create an object and spring-boot will automatically set those request parameters on it.
#GetMaping
public ResponseEntity<?> listEntities(#RequestParam(value = "page-number", defaultValue = "0") #Min(0) Integer pageNumber,
#RequestParam(value = "page-size", defaultValue = "100") #Min(1) Integer pageSize ... )
I am thinking to create a class called RequestParamsDTO, where I'd have my query params responsible for the pagination.
But in order to have those fields set on the RequestParamsDTO, I'd have to match the name of the request param with the field name. But it won't be a valid variable name: page-size.
There must be some workaround, similar to #RequestParam's value attribute, that would set given request param on my field in the DTO.
Please advise.
Someone already purposed this feature before such that you can do the following .But unfortunately it is declined due to inactivity response :
public class RequestParamsDTO{
#RequestParam(value="page-number",defaultValue="0")
#Min(0)
private Integer pageNumber;
#RequestParam(value = "page-size", defaultValue = "100")
#Min(1)
Integer pageSize
}
The most similar things that you can do is using its #ModelAttribute which will resolve the parameter in the following orders:
From the model if already added by using Model.
From the HTTP session by using #SessionAttributes.
From a URI path variable passed through a Converter (see the next example).
From the invocation of a default constructor.
From the invocation of a “primary constructor” with arguments that match to Servlet request parameters. Argument names are determined through JavaBeans #ConstructorProperties or through runtime-retained parameter names in the bytecode.
That means the RequestParamsDTO cannot not have any default constructor (constructor that is without arguments) .It should have a "primary constructor" which you can use #ConstructorProperties to define which request parameters are mapped to the constructor arguments :
public class RequestParamsDTO{
#Min(0)
Integer pageNumber;
#Min(1)
Integer pageSize;
#ConstructorProperties({"page-number","page-size"})
public RequestParamsDTO(Integer pageNumber, Integer pageSize) {
this.pageNumber = pageNumber != null ? pageNumber : 0;
this.pageSize = pageSize != null ? pageSize : 100;
}
}
And the controller method becomes:
#GetMaping
public ResponseEntity<?> listEntities(#Valid RequestParamsDTO request){
}
Notes:
There is no equivalent annotation for #RequestParam 's defaultValue,so need to implement in the constructor manually.
If the controller method argument does not match the values in this , it will resolved as #ModelAttribute even though #ModelAttribute is not annotated on it explicitly.
To be honest this seems like a lot of effort for a functionality that exists already in spring-boot. You can either extend your repositories from PagingAndSortingRepository and have pagination added whenever you call a collection resource.
Or you can write a custom query method (or overwrite an existing one) and add this:
Page<Person> findByFirstname(String firstname, Pageable pageable);
This way spring boot will automatically add all those parameters you want to the Request.

Thymeleaf add multiple parameters to URL in one go

Given I have MVC endpoint with mapping:
#RequestMapping
public String eventHistory(final Model model,
#PageableDefault(sort = {"id"}, direction = DESC) final Pageable pageable,
final EventHistoryFilter eventHistoryFilter) {
...
model.addAttribute("eventHistoryFilter", eventHistoryFilter);
}
where EventHistoryFilter is:
final class EventHistoryFilter {
private Long eventId;
private String eventType;
private String eventDateFrom;
private String eventDateTo;
...
}
and in thymeleaf template I would like to construct URL with parameters, e.g.:
th:href="#{/event-history(page=${page.number-1},eventId=${eventHistoryFilter.eventId},eventType=${eventHistoryFilter.eventType},eventDateFrom=${eventHistoryFilter.eventDateFrom},eventDateTo=${eventHistoryFilter.eventDateTo})}"
How can I add multiple parameters into URL in one go?
I didn't find it here: https://www.thymeleaf.org/doc/articles/standardurlsyntax.html#adding-parameters
I'd like to avoid specifying each parameter one by one.
EDIT:
I tried to use https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#building-uris-to-controllers
String url = MvcUriComponentsBuilder.fromMappingName("EHE#eventHistory").arg(2, eventHistoryFilter).build();
but resolved URL doesn't contain any request parameters.
and thymeleaf counterpart:
th:href="${#mvc.url('EHE#eventHistory').arg(2,__${eventHistoryFilter}__).build()}"
fails during evaluation.

Resources