Suppose we have this method
@RequestMapping(value= "list")
public HttpEntity<ListResource> getList(
@RequestParam(value = "text1", required = false) String text1,
@RequestParam(value = "text2", required = false) String text2,
@RequestParam(value = "text3", required = false) String text3,
...
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "10") Integer limit) {
// ...
}
Because we have a lot of parameters, we rewrote
@RequestMapping(value= "list")
public HttpEntity<ListResource> getList(RequestObject requestObject) {
// ...
}
public class RequestObject {
private String text1;
private String text2;
private String text3;
...
private Integer page;
private Integer limit;
}
Spring MVC maps automatically the RequestObject with given parameters
But when generating the Link
final RequestObject requestObject = new RequestObject();
requestObject.setPage(1);
requestObject.setLimit(10);
requestObject.setText1("test");
return ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(ABCController.class)
.getList(requestObject));
That doesn't return
http://localhost:8080/list?page=1&limit=10&text1=test
but only
http://localhost:8080/list
do you have any idea?
You don't actually call the method you show above. The method takes a lot of Strings, you hand in a RequestObject. Currently only parameters with @PathVariable or @RequestMapping are supported.
@olivergierke Hi, i would like to use the method that takes RequestObject, not the one taking a lot of String. I know that's not supported for now. Do you have any plan to support it like Spring MVC ?(RequestObject)
@khong07 pagination should be handled using a Pageable, maybe that would already reduce the number of parameters to a manageable amount?
If you dig deeper, there might even be a chance of implementing something yourself by implenting a UriComponentsContributor that handles your RequestObject, and registering it at ControllerLinkBuilderFactory. I haven't tried it, but it looks doable.
@otrosien my implementation still has a lot aof parameters :(
Yes, i think it's possible, thank for suggestion.
I would also welcome the support for RequestObjects because search APIs often use a variety of parameters. This is definitely cleaner to solve with RequestObjects. It also just became clear after a bunch few failed attempts to create proper links, that the support is still missing here.
Example:
@RequestMapping(value= "/search", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<PagedResources> search(SearchRequest searchRequest, Pageable pageable, PagedResourceAssembler pageAssembler) {
Page<SearchResult> pages = searchService.search(searchRequest);
Link link = ControllerLinkBuilder.linkTo(methodOn(Controller.class).search(searchRequest, pageable, pageAssembler).withSelfRel();
return ResponseEntity.ok(pageAssembler.toResource(pages, link));
}
public class RequestObject {
private String param1;
private String param2;
private String param3;
...
private String paramN;
}
Request like GET /search?param1=foo¶m2=bar¶m3=darth&...¶mN=vader&sort=param1,asc
should then produce e.g. a result like http://localhost:8080/search?param1=foo¶m2=bar¶m3=darth&...¶mN=vader&page=0&size=20&sort=param1,asc
But 2 issues here:
RequestObjectsOk, i found a rough-and-ready workaround:
PagedResourcesAssembler pagedResourcesAssembler = new PagedResourcesAssembler(new HateoasPageableHandlerMethodArgumentResolver(), null);
By initializing the PagedResourcesAssembler with null as the baseUri, the query params will not get ignored by the assembler.
I know this is definitively not the intended way to use it, but for now it's fine for me. The generated links are now correct. The only downside is that non-mapped query params are also present and won't get filtered out.
I've found another solution to get a single part of the desired behavior. Now the links are rendered correctly.
Just created my own ControllerLinkBuilderFactory and re-used existing functionality. This was already mentioned by @otrosien. Using a request object is still not possible, but the param list would be anyway wrong if you are trying to use snake_case names as Jackson annotations (attribute naming) are not considered currently as replacement for e.g. @RequestParam(value="param_1").
Here is the relevant code snippet:
ControllerLinkBuilderFactory controllerLinkBuilderFactory = new ControllerLinkBuilderFactory();
controllerLinkBuilderFactory.setUriComponentsContributors(Arrays.asList(new HateoasPageableHandlerMethodArgumentResolver()));
Link link = linkTo(methodOn(Controller.class).search(param1, param2, ..., paramN, pageable, pagedResourcesAssembler)).withSelfRel();
PagedResources pagedResources = pagedResourcesAssembler.toResource(page, link.expand());
Any updates on this issues? I still struggle with the same problem like @khong07
We faced this issue on our project and created a workaround - it's fairly specific to our project, but perhaps it could inspire others: https://gist.github.com/bob983/b26f7af740c6aa0c4913aec43b209c06
Please note that the project uses RxJava and thus uses injected UriComponentsBuilder because Spring Hateoas was trying to get hold of current request which wasn't really working :-)
any update on this? We are facing the same issue and would be grateful for a fix!
The reason for the glacial pace here is that there's no real way for us to tell which of the arguments in something like this:
search(SearchRequest searchRequest, Pageable pageable, PagedResourceAssembler pageAssembler) { … }
is one that actually binds request parameters. In fact, SearchRequest is not bound via implicit @RequestParam but via the implicit @ModelAttribute which is a bit unusual use in REST APIs as they usually switch to using the request payload for information transfer. We can definitely investigate what it would mean to also support handler method parameters explicitly annotated with @ModelAttribute but it'll certainly add quite a bit of complexity (how to handle complex properties declared in the type that maps the parameters? etc.) and I am not sure what that's doing to our affordances calculation etc.
Most helpful comment
I would also welcome the support for RequestObjects because search APIs often use a variety of parameters. This is definitely cleaner to solve with RequestObjects. It also just became clear after a bunch few failed attempts to create proper links, that the support is still missing here.
Example:
Request like
GET /search?param1=foo¶m2=bar¶m3=darth&...¶mN=vader&sort=param1,ascshould then produce e.g. a result like
http://localhost:8080/search?param1=foo¶m2=bar¶m3=darth&...¶mN=vader&page=0&size=20&sort=param1,ascBut 2 issues here:
RequestObjects