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

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.

Related

Wrapper type Path variable won't accept null value - 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) {
}

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.

call a static method on a parameter while using #CacheResult

I need to cache the result of a method that call a webservice.
The method signature is like this :
public Result search(long id, String name, Date date);
and the result depends on all the parameters
I created the ehcache configuration for the cache myCache, normally to use this cache I should use the #CacheResult annotation :
#CacheResult(cacheName = "myCache")
public Result search(long id, String name, Date date);
But in my case I need to call a static method on the date parameter, I want to do it the same way as the #Cacheable annotation :
#Cacheable(value = "myCache", key ="{#id, #name, T(my.static).method(#date)}")
public Result search(long id, String name, Date date);
My question is how could I call a static method on a parameter while using #CacheResult ?
#CacheResult supplies a way to customize the generated key by defining a key generator class like this:
#CacheResult(cacheKeyGenerator = CustomKeyGenerator.class)
public Result search(long id, String name, Date date);
It does not support definition of key generation in terms of SpEL evaluation directly in the annotation; instead you must provide your own implementation of javax.cache.annotation.CacheKeyGenerator:
public class CustomKeyGenerator implements CacheKeyGenerator {
#Override
public GeneratedCacheKey generateCacheKey(CacheKeyInvocationContext<? extends Annotation> cacheKeyInvocationContext) {
CacheInvocationParameter[] parameters = cacheKeyInvocationContext.getKeyParameters();
// calculate a key based on parameters
return new SearchKey();
}

jpa query with optional parameters

I m using Spring Data Rest JPA which implements the Query internally on the basis of method name.
I wrote the following method in my repository interface which should list all the users in a state and if the name and/or age is present, it should filter the result.
StateId is mandatory but name and age are optional filter parameters
public List<User> findByStateIdAndNameOrAge(Integer stateId, String name , Integer age, Pageable pageable);
I am not getting any results. Where am I doing wrong?
You can try
There is no mistake in your method defination.
public List<User> findByStateIdAndNameOrAge(Integer stateId, String name , Integer age, Pageable pageable);
but you can't pass null parameter to this method so it will not work if you are putting any parameter is blank.

obscuring url strings in spring mvc

How do I obscure the values of fields used in url strings in a spring mvc web app?
For example, if I want to send the record with recordID=1 into the view, I give the user a hyperlink with the following url:
https://myapp.com/urlpattern?recordID=1
As you can see, this not only exposes the recordID=1, it also tempts a malicious user to start typing other numbers to mine other records such as recordID=5 or recordID=9.
Does the spring framework or spring security have a built-in way of encrypting url strings? Or do I need to change the id values in the underlying database using hibernate?
The controller code for the above url pattern is:
#RequestMapping(value = "/urlpattern", method = RequestMethod.GET)
public String processUrlPattern(#RequestParam("recordID") String recordId,
HttpServletRequest request, BindingResult result, Map<String, Object> model) {
Long recId = Long.valueOf(recordId).longValue();
RecordObject sel_record = this.appService.findRecordById(recId);
model.put("sel_record", sel_record);
return "foldername/jspname";
}
Note that all entities in the app inherit from the same BaseEntity whose id-generating code is as follows:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#DiscriminatorFormula("(CASE WHEN dtype IS NULL THEN 'BaseEntity' ELSE dtype END)")
#org.hibernate.annotations.DiscriminatorOptions(force=true)
public abstract class BaseEntity {
#Transient
private String dtype = this.getClass().getSimpleName();
#Id
#GeneratedValue(strategy=GenerationType.TABLE, generator="TBL_GEN")
#TableGenerator(
name="TBL_GEN",
table="GENERATOR_TABLE",
pkColumnName = "mykey",
valueColumnName = "hi",
pkColumnValue="id",
allocationSize=20
)
protected Integer id;
//other stuff
}
NOTE: All the users are authenticated/authorized using Spring security. However, the data is very sensitive, and it is important that no one be able to manipulate url strings.
Use HDIV, it does this out of the box:
http://hdiv.org/hdiv-documentation-single/doc.html
"A6 (Sensitive data exposure) : HDIV offers a confidentially property to all data generated at sever side. That is to say, HDIV replace original parameter values generated at server side by relative values (0,1,2,4, etc.) that avoid exposing critical data to the client side."

Categories

Resources