I've been trying to get a simple REST API to list contents of a collection and I'm using matrix variables to control the pagination.
My controller has the following method to list contents of a collection:
#RequestMapping(
value = "articles",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ArticlePageRestApiResponse listArticles(
#MatrixVariable(required = true, defaultValue = 100, value = "resultsPerPage") int resultsPerPage,
#MatrixVariable(required = true, defaultValue = 0, value = "pageNumber") int pageNumber) {
// some logic to return the collection
}
If I then do a GET http://example.com/articles;resultsPerPage=22;pageNumber=33 it fails to find a request mapping. I have enabled matrix variables support by adding the following:
#Configuration
public class EnableUriMatrixVariableSupport extends WebMvcConfigurationSupport {
#Override
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping hm = super.requestMappingHandlerMapping();
hm.setRemoveSemicolonContent(false);
return hm;
}
}
What I've found is that if the matrix variables are prefixed with at least one template variable then the matrix variables are correctly assigned. The following works but is ugly where I've had to make part of the URI path a template variable that is always going to be "articles" to trick the Request Mapping Handler into thinking there is at least one URI template variable :
#RequestMapping(
value = "{articles}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ArticlePageRestApiResponse listArticles(
#PathVariable("articles") String ignore,
#MatrixVariable(required = true, defaultValue = 100, value = "resultsPerPage") int resultsPerPage,
#MatrixVariable(required = true, defaultValue = 0, value = "pageNumber") int pageNumber) {
// some logic to return the collection
}
Have I found a bug or am I mis-understanding matrix variables?
According to Spring documentation
If a URL is expected to contain matrix variables, the request mapping
pattern must represent them with a URI template. This ensures the
request can be matched correctly regardless of whether matrix
variables are present or not and in what order they are provided.
In your first example you use no templates (like {articles}) in URL mapping, so Spring is unable to detect matrix parameters.
I'd rather call it not a bug, but an implemnetation side effect. We have it just because #MatrixVariable support is build on the top of the old #PathVariable parsing mechanism.
Related
I have Spring Boot application with version 2.3.0.
and springdoc-openapi-webflux-ui in version 1.4.1.
I have annotated parameter in operation like this.
parameters = {
#Parameter(
hidden = true, schema = #Schema(implementation = Boolean.class),
in = ParameterIn.QUERY, name = DO_NOT_FORWARD
)
With hidden = true I expected that this parameter will not be visible in swagger-ui. But it is.
Did I misunderstood this parameter or is it not doing what it was supposed to do?
I want this parameter to be in api-docs, to have generated client able to use this parameter, but I want it invisible in swagger-ui
Try
#Parameter(name = "paramName", hidden = true)
#GetMapping("/example")
public Object example(String paramName) {
return null;
}
instead of
#Operation(
parameters = {
#Parameter(name = "paramName", hidden = true)
}
)
#GetMapping("/example")
public Object example(String paramName) {
return null;
}
You just make sure that the name of in the #Parameter annotation, is the exact name of the operation parameter you want to hide.
You can have a look at the documentation as well:
https://springdoc.org/faq.html#how-can-i-hide-a-parameter-from-the-documentation-
If you are still having coniguration issue, you can add the code of sample HelloController that reproduces your problem, or you can add the link to a minimal, reproducible sample in github.
As per the doc
#GetMapping("/example")
fun someCall: Response (
#Parameter(hidden = true) String paramName
) {
return response;
}
I'm trying to pass in a bunch of id's to create a filter.
The incoming request looks like (ignoring the pagination stuff, which works fine)
http://localhost:8080/news-items?filter%5B%5D=09c731de-7ed8-385d-849c-f4d6535137ab&filter%5B%5D=dd1ba187-2df9-3985-ad1c-a4cde2dfe669&modelPath=controller.newsItems&page=0&per_page=25
Where the filter param equals an ID, but there is a bunch of them, for example:
filter: [
"09c731de-7ed8-385d-849c-f4d6535137ab",
"dd1ba187-2df9-3985-ad1c-a4cde2dfe669"
],
I can't seem to collect the filters in the controller. At the moment I have
public String getFeeds(#RequestParam(value = "filter", required = false) MultiValueMap<String, String> filter, #RequestParam(value = "page", required = false) int page, #RequestParam(value = "per_page", required = false) int perPage) {
log.info("Filter: {}", filter);
}
However filter is always null. I've tried using a String rather than a map but that is also always null.
How do I go about accepting an unknown number of params in this manner? I get the feeling this is really simple but I'm just missing the obvious...
Turns out it was simple like I thought. When using a Map in the #RequestParam it takes all the incoming params, regardless of what they are.
So from what I can tell the correct solution is to do something like
#GetMapping(produces = APPLICATION_JSON)
public String getFeeds(#RequestParam MultiValueMap<String, String> params) {
params.forEach(//something);
}
I think what you are looking for is just an Array or a List, something like below :
public String getFeeds(#RequestParam(value = "filter", required = false) List<String> filters) { ... }
Previously, I had Spring controller as below.
#RequestMapping(method = GET)
public List<TheResponse> getResponses(
#RequestParam(defaultValue = "1") int offset,
#RequestParam(defaultValue = "10") int limit) {
Pagination pagination = new Pagination(offset, limit);
...
return someResponse;
}
Swagger was generating document of this method with correct parameters information.
Later I created PaginationArgResolver by implementing HandlerMethodArgumentResolver. After that, I have to update the method as below, and apply #ApiImplicitParams to make it work with Swagger.
#ApiImplicitParams({
#ApiImplicitParam(name = "offset", dataType = "int", defaultValue = "1"),
#ApiImplicitParam(name = "limit", dataType = "int", defaultValue = "10")
})
#RequestMapping(method = GET)
public List<TheResponse> getResponses(#ApiIgnore Pagination pagination) {
...
}
Is there anyway #ImplicitParams is applied automatically whenever Pagination type argument is found?
OR
If I expose #PaginationSupported annotation, can I process it to achieve same results?
I am currently using springfox v2.4.0.
PS. I can edit source of Pagination class, in case some swagger annotation is needed there.
Why adding #ApiIgnore springfox will resolve these attributes inside the class automatically. When you want to add default values and other stuff you can add the #ApiParam annotation to the class attributes as well.
class Pagination {
#ApiParam(defaultValue = "1")
private int offset;
// [..]
}
I want somes params on swagger have its own default value. Is there anyway to give a default value ?
like this
this note has to be filled automatically with "stackoverflow"
You have to annotate your note parameter with defaultValue="stackoverflow".
You method signature should look like this:
public ResponseEntity<?> yourMethod(#ApiParam(value = "Default value for note", required = true, defaultValue = "stackoverflow") #Valid #RequestParam final String note) { }
I am using Spring MVC in my project and while mapping user request to a URI, I get the 404 error. Here is my function skeleton which I want to be invoked:
#RequestMapping(value="/inventory/discovery", method = RequestMethod.GET, params = {"discoveryType"}, produces = {"application/json"})
public String getDiscoveryByType(#RequestParam("discoveryType") String discoveryType)
{
return discoveryType;
}
I am expecting this method to be called when I give the URL
http://<some-ip>/inventory/discovery/discoveryType?=DMVPN
However, when I test the code using Chrome's Advanced Rest Client I see that the URI it is trying to access is "/inventory/DMVPN" and not the "/inventory/discovery?discoveryType=DMVPN".
Am I missing something here? I don't see anything wrong with my URL syntax
I have another function in my code (but I don't think it is causing the problem) which has same request-mapping value, but has no params attribute.
#RequestMapping(value = "/inventory/discovery", method = RequestMethod.GET, produces = { "application/json" })
public ResponseEntity<DiscoveryNIOListResult> getAllDiscovery() {
logger.trace("getAllDiscovery");
List<DiscoveryNIO> listDiscoveryNIO = discoveryDasClient.getDiscoveryList();
DiscoveryNIOListResult result = new DiscoveryNIOListResult();
result.setResponse(listDiscoveryNIO);
return new ResponseEntity<DiscoveryNIOListResult>(result, HttpStatus.OK);
}
Ok, so a second look at your URL:
http://<some-ip>/inventory/discovery/discoveryType?=DMVPN
This is NOT passing discoveryType as a request GET parameter. For that you would need to do:
http://<some-ip>/inventory/discovery&discoveryType?=DMVPN
If you want to use path variables instead, you could do something like:
http://<some-ip>/inventory/discovery/DMVPN
And change your handler to something like:
#RequestMapping(value="/inventory/discovery/{discoveryType}", method = RequestMethod.GET, produces = {"application/json"})
public String getDiscoveryByType(#PathVariable("discoveryType") String discoveryType)
{
return discoveryType;
}
It looks like you were trying to mix these two methods, and that wont work.
Try changing your URL to:
http://<some-ip>/inventory/discovery?discoveryType=DMVPN