spring boot pageable configure params name - spring

I am using Spring Boot v1.5.2.RELEASE.
My controller is like this:
#PostMapping(path = "/list_praxis")
public
#ResponseBody
ModelAndView login(Pageable pageable,HttpServletRequest request) {
return new ModelAndView(new WebJsonView());
}
My parameters size:10 page:0, Pageable works fine.
But now I want to change my parameters to pageSize:10 currentPage:0.
Pageable doesn't work, because pageable can only receive size and page, and doesn't support other parameter names.
How can I configure this to use pageSize and currentPage instead of size and page?

You can also use spring's application properties like this
spring.data.web.pageable.page-parameter: pageNumber
spring.data.web.pageable.size-parameter: pageSize
You can learn about other common application properties here.

I think this would work for you.
#Configuration
public class RepositoryRestMvcConfiguration extends SpringBootRepositoryRestMvcConfiguration{
#Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setPageParamName("p")
.setLimitParamName("l")
.setSortParamName("s");
}
}
This configuration would set the paging parameter to p, the size parameter to l, and the sort parameter to s:

Related

Dynamically securing Spring Rest endpoint

Actually my code look like that:
#PreAuthorize("hasAuthority('admin')")
#RequestMapping(value = "/xxxx", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<> method(#RequestBody RequestClass request) {
}
As you can see the allowed authorities are hard-coded in java code.
Is there a way to override the behaviour of PreAuthorize or to load the proper endpoint configuration at startup from an external source(database or configuration file)?
I suppose you might give something like this a try (might need some tweaking) but it's ugly and I would not do it myself...
Setup your method to role mappings in your configuration. For example:
permissions.method1: admin
permissions.method2: admin
permissions.method3: user
Then use an #ConfigurationProperties class to load in your map into a Map.
#ConfigurationProperties("")
public class SecurityMappingProperties {
private final Map<String, String> permissions = new HashMap<>();
public Map<String, String> getPermissions() {
return permissions;
}
}
Then setup a service to handle the lookup.
#Service
public class MethodPermissionService {
#Autowired
private SecurityMappingProperties mappingProperties;
//lookup the mapped role and see if you user has it..
public Boolean lookupPermissionForMethod(String method){
return doesUserHaveRole(mappingProperties.get(method));
}
private Boolean doesUserHaveRole(String role){
//implement whatever logic you want to look up the requesting user's role...
}
}
Then in your controllers, invoke the methodPermissionService and pass in the method name, like so...
#PreAuthorize("#methodPermissionService('method1')")
This, of course, would require you to have every secured method in all of your controllers to have an #Preauthorize with the matching method name as the argument to the methodPermissionService('xxx').
Since we are already in this rabbit hole, if you really wanted to, you could also just have a single place to declare all of them in some sort of MethodRoleHolder class where you can make them static Strings like the following:
public static final String METHOD1_SECURITY = "#methodPermissionService('method1')";
public static final String METHOD2_SECURITY = "#methodPermissionService('method2')";
then use them in your controllers...
#PreAuthorize(MethodRoleHolder.METHOD1_SECURITY)
Upfront caveat: I haven't actually tried this myself exactly as I laid out here but I have implemented a security scheme similar to this, just without the dynamic role mapping look up part.

Is there a way to restrict Pageable's size and page values passed to controller method by Spring?

I am using following controller method
public String listUsers(#PageableDefault(size = 20, page = 0) Pageable pageable)
And obviously, we can controll page and size with query params size and page. But lets say, we want to protect ourselfs from dumping whole database in one page by user or due to tempering. Is there a limit, that can be set to Pageable object eg. #DefaultPageableConstrains or I have to validate size by hand?
I just would like to make use of Spring validation mechanisms like #Valid #Min or #Max annotations.
The default maxPageSize is 2000. This can be overriden like so:
#Configuration
public class PaginationConfiguration extends SpringDataWebConfiguration {
private final int MAX_PAGE_SIZE = 200;
#Bean
#Override
public PageableHandlerMethodArgumentResolver pageableResolver() {
PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver =
new PageableHandlerMethodArgumentResolver(sortResolver());
pageableHandlerMethodArgumentResolver.setMaxPageSize(MAX_PAGE_SIZE);
return pageableHandlerMethodArgumentResolver;
}
}

Spring MVC RestController allow params with different names in methods

I am writing an API using Spring MVC and I am coming up with a problem allowing apps written in different languages to consume my API.
It turns out that the "Ruby users" like to have their params named in snake_case and our "Java users" like to have their param names in camel_case.
Is it possible to create my methods that allow param names to be named multiple ways, but mapped to the same method variable?
For instance... If I have a method that accepts a number of variables, of them there is mapped to a postal code. Could I write my method with a #RequestParam that accepts BOTH "postal_code" and "postalCode" and maps it to the same variable?
Neither JAX-RS #QueryParam nor Spring #RequestParam support your requirement i.e., mapping multiple request parameter names to the same variable.
I recommend not to do this as it will be very hard to support because of the confusion like which parameter is coming from which client.
But if you really wanted to handle this ((because you can't change the URL coming from 3rd parties, agreed long back), then the alternative is to make use of HandlerMethodArgumentResolver which helps in passing our own request argument (like #MyRequestParam) to the controller method like as shown in the below code:
Controller class:
#Controller
public class MyController {
#RequestMapping(value="/xyz")
public void train1(#MyRequestParam String postcode) {//custom method argument injected
//Add your code here
}
}
MyRequestParam :
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface MyRequestParam {
}
HandlerMethodArgumentResolver Impl class:
public class MyRequestParamWebArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
MyRequestParam myRequestParam =
parameter.getParameterAnnotation(MyRequestParam.class);
if(myRequestParam != null) {
HttpServletRequest request =
(HttpServletRequest) webRequest.getNativeRequest();
String myParamValueToBeSentToController = "";
//set the value from request.getParameter("postal_code")
//or request.getParameter("postalCode")
return myParamValueToBeSentToController;
}
return null;
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.getParameterAnnotation(MyRequestParam.class) != null);
}
}
WebMvcConfigurerAdapter class:
#Configuration
class WebMvcContext extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyRequestParamWebArgumentResolver());
}
}
I think what you want to do is not allowed by Spring framework with the annotation RequestParam.
But if you can change the code or say to your third party to modify the calls i would suggest you 2 options
Option 1:
Use the #PathVariable property
#RequestMapping(value = "/postalcode/{postalCode}", method = RequestMethod.GET)
public ModelAndView yourMethod(#PathVariable("postalCode") String postalCode) {
//...your code
Here does not matter if the are calling your URL as:
http://domain/app/postalcode/E1-2ES
http://domain/app/postalcode/23580
Option 2:
Create 2 methods in your controller and use the same service
#RequestMapping(value = "/postalcode", method = RequestMethod.GET, params={"postalCode"})
public ModelAndView yourMethod(#RequestParam("postalCode") String postalCode) {
//...call the service
#RequestMapping(value = "/postalcode", method = RequestMethod.GET, params={"postal_code"})
public ModelAndView yourMethodClient2(#RequestParam("postal_code") String postalCode) {
//...call the service
If is possible, I would suggest you option 1 is much more scalable

Map #CookieValue, #RequestHeader etc. to POJO in Spring Controller

I have a bunch of params in my controller and want to map all of them to a separate POJO to keep readability. There is also a #CookieValue, #RequestHeader I need to evaluate and aim for a solution to also map them to that POJO. But how?
I saw a possible solution on a blog but it doesn't work, the variable stays null.
Controller:
#RequestMapping(path = MAPPING_LANGUAGE + "/category", produces = MediaType.TEXT_HTML_VALUE)
#ResponseBody
public String category(CategoryViewResolveModel model) {
doSomething();
}
And my POJO is this:
public class CategoryViewResolveModel {
private String pageLayoutCookieValue;
public CategoryViewResolveModel() {
}
public CategoryViewResolveModel(
#CookieValue(value = "SOME_COOKIE", required = false) String pageLayoutCookieValue) {
this.pageLayoutCookieValue = pageLayoutCookieValue;
}
... some other RequestParams, PathVariables etc.
}
According to the documentation it's not possible for #CookieValue and #RequestHeader.
This annotation is supported for annotated handler methods in Servlet
and Portlet environments.
Take a look at:
https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-creating-a-custom-handlermethodargumentresolver/
instead of using getParameter to access request parameters you can use getHeader to retrieve the header value and so define your CategoryViewResolveModel just as you were requesting

Set default page size for JPA Pageable Object

I have a PagingandSorting Repository which has a method that accecpts a pageable object.
I also have a controller that accepts a pageable object through the URL.
My use case is that, if a user specifies a page size parameter in the URL i must take that value for the pageable object. If he does not mention take a default value of 50.
But the pageable object defaults to 20 right now.
Any Suggestions would help
If you are talking about a Spring Data PagingAndSortingRepository you can set the default page size by using the #PageableDefault on a Controller method as follows:
public String listClients(#ModelAttribute FilterForm form, Model model, WebRequest request, #PageableDefault(sort = { "surname",
"forename", "address.town" }, value = 50) Pageable pageable) {
}
Or you can configure a global default using the following in your Spring config as shown below in both XML and Java config.
Note that newer versions of Spring Data use zero based page indexing while older versions used 1 for the first page. If your UI paging library expects 1 as first page then you can set the oneIndexedParameters property to true:
public void setOneIndexedParameters(boolean oneIndexedParameters)
Configures whether to expose and assume 1-based page number indexes in
the request parameters. Defaults to false, meaning a page number of 0
in the request equals the first page. If this is set to true, a page
number of 1 in the request will be considered the first page.
Parameters: oneIndexedParameters - the oneIndexedParameters to set
public void setFallbackPageable(Pageable fallbackPageable)
Configures the Pageable to be used as fallback in case no PageableDefault or
PageableDefaults (the latter only supported in legacy mode) can be
found at the method parameter to be resolved. If you set this to null,
be aware that you controller methods will get null handed into them in
case no Pageable data can be found in the request. Note, that doing so
will require you supply bot the page and the size parameter with the
requests as there will be no default for any of the parameters
available.
Parameters: fallbackPageable - the Pageable to be used as general
fallback.
In XML this looks like the following then:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.PageableHandlerMethodArgumentResolver">
<property name="oneIndexedParameters" value="true"/>
<property name="fallbackPageable">
<bean class="org.springframework.data.domain.PageRequest">
<constructor-arg name="page" value="1" />
<constructor-arg name="size" value="10" />
</bean>
</property>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
In Java Config this looks like the below:
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setOneIndexedParameters(true);
resolver.setFallbackPageable(new PageRequest(1, 20));
argumentResolvers.add(resolver);
super.addArgumentResolvers(argumentResolvers);
}
}
For Spring Boot 2.X you have set of parameters:
# DATA WEB (SpringDataWebProperties)
spring.data.web.pageable.default-page-size=20 # Default page size.
spring.data.web.pageable.max-page-size=2000 # Maximum page size to be accepted.
spring.data.web.pageable.one-indexed-parameters=false # Whether to expose and assume 1-based page number indexes.
spring.data.web.pageable.page-parameter=page # Page index parameter name.
spring.data.web.pageable.prefix= # General prefix to be prepended to the page number and page size parameters.
spring.data.web.pageable.qualifier-delimiter=_ # Delimiter to be used between the qualifier and the actual page number and size properties.
spring.data.web.pageable.size-parameter=size # Page size parameter name.
spring.data.web.sort.sort-parameter=sort # Sort parameter name.
You can set below in application.yml
spring.data.rest.default-page-size: 50
You can use this Annotation before your Pageable param:
#PageableDefault(size = 40)
// so your parameter should be like this:
#PageableDefault(size = 40) Pageable pageable
** Update:
You can store 40 in application.yml file and use it in the whole project.
in application.yml :
pageSize: 40
then:
// so your parameter should be like this:
#PageableDefault(size = ${pageSize}) Pageable pageable
And, for completeness, here is an example for a Spring Boot configuration. In the #Configuration class that extends WebMvcConfigurerAdapter, set the default page size to 50 items like this:
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setFallbackPageable(new PageRequest(0, 50));
argumentResolvers.add(resolver);
super.addArgumentResolvers(argumentResolvers);
}
This still isn't well documented but for anyone else finding this article, the RepositoryRestConfigurerAdapter has all the spring data rest config there.
#Configuration
public static class RestConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setDefaultPageSize(50);
}
}
In Spring Boot 2.1.6.RELEASE, you can use below:
#Configuration
public class WebConfig implements WebMvcConfigurer{
#Value("${paging.default.pageSize}")
private int size;
#Value("${paging.default.page}")
private int page;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setFallbackPageable(PageRequest.of(page, size));
resolvers.add(resolver);
WebMvcConfigurer.super.addArgumentResolvers(resolvers);
}
}
Provided answers are very good and should help in most cases. However, I use slightly different approach, which allows me to use different default page size per model and can be configurable with Spring or system properties.
Please note that this approach has one fundamental limitation, namely, it does not accept any size coming with a request; it uses sorting information though. So if you need ability to change number of returned items per page via request parameters, this solution is not for you.
First of all, I created a utility class (or just a method in a controller) which creates a new Pageable instance base on a request Pageable and configured page size
public static Pageable updatePageable(final Pageable source, final int size)
{
return new PageRequest(source.getPageNumber(), size, source.getSort());
}
In a controller I add a variable which holds my default page size (in this case default value is 20 if configuration is not provided):
#Value("${myapplication.model.items-per-page:20}")
private int itemsPerPage;
And then I override (i.e. create a new Pageable instance) default page size in request handling method:
#RequestMapping(method = RequestMethod.GET)
public Page<Model> websites(final Pageable pageable)
{
return repository.findAll(updatePageable(pageable, itemsPerPage));
}
I use different default page size variables for different models / controllers which then can be configured even from system properties.
If there is someone who is trying to use #PageableDefault with spring.data.web.pageable.default-page-size and don't know why the default page size you set ins't working
public #interface PageableDefault {
/**
* The default-size the injected {#link org.springframework.data.domain.Pageable} should get if no corresponding
* parameter defined in request (default is 10).
*/
int size() default 10;
}
As you can see the above code if you use #PageableDefault the default page size is set to 10 no matter what you set spring.data.web.pageable.default-page-size: 99
So if you want to provide default sort condition in a simple way with page size , you can use something like this
#PageableDefault(size = 99, sort = {"createdAt"}, direction = sort.Direction.DESC) Pageable pageable

Resources