Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8d85e0e

Browse files
Duplicate key class Parameter when documenting two GET methods with same path and PathVariable, differing only by produces media type. Fixes #3073
1 parent 4d505d6 commit 8d85e0e

File tree

5 files changed

+228
-1
lines changed

5 files changed

+228
-1
lines changed

‎springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
import io.swagger.v3.oas.models.parameters.Parameter;
6969
import io.swagger.v3.oas.models.parameters.RequestBody;
7070
import org.apache.commons.lang3.StringUtils;
71+
import org.slf4j.Logger;
72+
import org.slf4j.LoggerFactory;
7173
import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
7274
import org.springdoc.core.customizers.ParameterCustomizer;
7375
import org.springdoc.core.customizers.SpringDocCustomizers;
@@ -118,6 +120,11 @@
118120
*/
119121
public abstract class AbstractRequestService {
120122

123+
/**
124+
* The constant LOGGER.
125+
*/
126+
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRequestService.class);
127+
121128
/**
122129
* The constant PARAM_TYPES_TO_IGNORE.
123130
*/
@@ -407,7 +414,12 @@ else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.
407414
*/
408415
private LinkedHashMap<ParameterId, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<ParameterId, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
409416
LinkedHashMap<ParameterId, Parameter> map = operationParameters.stream().collect(Collectors.toMap(ParameterId::new, parameter -> parameter, (u, v) -> {
410-
throw new IllegalStateException(String.format("Duplicate key %s", u));
417+
LOGGER.warn(
418+
"Duplicate OpenAPI parameter detected: name='{}', in='{}'. Keeping the first found and ignoring the rest. " +
419+
"Declare the parameter only once.",
420+
u.getName(), u.getIn()
421+
);
422+
return u;
411423
}, LinkedHashMap::new));
412424

413425
for (Entry<ParameterId, io.swagger.v3.oas.annotations.Parameter> entry : parametersDocMap.entrySet()) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * *
7+
* * * * * * Copyright 2019-2025 the original author or authors.
8+
* * * * * *
9+
* * * * * * Licensed under the Apache License, Version 2.0 (the "License");
10+
* * * * * * you may not use this file except in compliance with the License.
11+
* * * * * * You may obtain a copy of the License at
12+
* * * * * *
13+
* * * * * * https://www.apache.org/licenses/LICENSE-2.0
14+
* * * * * *
15+
* * * * * * Unless required by applicable law or agreed to in writing, software
16+
* * * * * * distributed under the License is distributed on an "AS IS" BASIS,
17+
* * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* * * * * * See the License for the specific language governing permissions and
19+
* * * * * * limitations under the License.
20+
* * * * *
21+
* * * *
22+
* * *
23+
* *
24+
*
25+
*/
26+
27+
package test.org.springdoc.api.v31.app192;
28+
29+
public class Feature {
30+
31+
private String name;
32+
33+
public String getName() {
34+
return name;
35+
}
36+
37+
public void setName(String name) {
38+
this.name = name;
39+
}
40+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * *
7+
* * * * * * Copyright 2019-2025 the original author or authors.
8+
* * * * * *
9+
* * * * * * Licensed under the Apache License, Version 2.0 (the "License");
10+
* * * * * * you may not use this file except in compliance with the License.
11+
* * * * * * You may obtain a copy of the License at
12+
* * * * * *
13+
* * * * * * https://www.apache.org/licenses/LICENSE-2.0
14+
* * * * * *
15+
* * * * * * Unless required by applicable law or agreed to in writing, software
16+
* * * * * * distributed under the License is distributed on an "AS IS" BASIS,
17+
* * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* * * * * * See the License for the specific language governing permissions and
19+
* * * * * * limitations under the License.
20+
* * * * *
21+
* * * *
22+
* * *
23+
* *
24+
*
25+
*/
26+
27+
package test.org.springdoc.api.v31.app192;
28+
29+
import java.util.List;
30+
31+
import io.swagger.v3.oas.annotations.Operation;
32+
import io.swagger.v3.oas.annotations.Parameter;
33+
import io.swagger.v3.oas.annotations.enums.ParameterIn;
34+
import reactor.core.publisher.Flux;
35+
36+
import org.springframework.http.MediaType;
37+
import org.springframework.web.bind.annotation.GetMapping;
38+
import org.springframework.web.bind.annotation.PathVariable;
39+
import org.springframework.web.bind.annotation.RequestMapping;
40+
import org.springframework.web.bind.annotation.RestController;
41+
42+
/**
43+
* @author bnasslahsen
44+
*/
45+
@RestController
46+
@RequestMapping
47+
public class HelloController {
48+
49+
@Operation(operationId = "getFeaturesJson",
50+
parameters =
51+
@Parameter(name = "organizationId" , description = "toto", in = ParameterIn.PATH))
52+
@GetMapping(value = "/{organizationId}/features", produces = MediaType.APPLICATION_JSON_VALUE)
53+
public List<Feature> getFeaturesAsJson(
54+
@PathVariable String organizationId) {
55+
return List.of(/* ... */);
56+
}
57+
58+
@Operation(operationId = "getFeaturesNdjson",
59+
parameters = @Parameter(name = "organizationId", description = "titi", in = ParameterIn.PATH
60+
))
61+
@GetMapping(value = "/{organizationId}/features", produces = "application/x-ndjson")
62+
public List<Flux> getFeaturesAsNdjson(
63+
@PathVariable String organizationId) {
64+
return null;
65+
}
66+
67+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * *
7+
* * * * * * Copyright 2019-2025 the original author or authors.
8+
* * * * * *
9+
* * * * * * Licensed under the Apache License, Version 2.0 (the "License");
10+
* * * * * * you may not use this file except in compliance with the License.
11+
* * * * * * You may obtain a copy of the License at
12+
* * * * * *
13+
* * * * * * https://www.apache.org/licenses/LICENSE-2.0
14+
* * * * * *
15+
* * * * * * Unless required by applicable law or agreed to in writing, software
16+
* * * * * * distributed under the License is distributed on an "AS IS" BASIS,
17+
* * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* * * * * * See the License for the specific language governing permissions and
19+
* * * * * * limitations under the License.
20+
* * * * *
21+
* * * *
22+
* * *
23+
* *
24+
*
25+
*/
26+
27+
package test.org.springdoc.api.v31.app192;
28+
29+
import test.org.springdoc.api.v31.AbstractSpringDocTest;
30+
31+
import org.springframework.boot.autoconfigure.SpringBootApplication;
32+
import org.springframework.context.annotation.ComponentScan;
33+
34+
public class SpringDocApp192Test extends AbstractSpringDocTest {
35+
36+
@SpringBootApplication
37+
@ComponentScan(basePackages = { "org.springdoc", "test.org.springdoc.api.v31.app192" })
38+
static class SpringDocTestApp {}
39+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{
2+
"openapi": "3.1.0",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/{organizationId}/features": {
15+
"get": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"operationId": "getFeaturesJson",
20+
"parameters": [
21+
{
22+
"name": "organizationId",
23+
"in": "path",
24+
"description": "titi",
25+
"required": true,
26+
"schema": {
27+
"type": "string"
28+
}
29+
}
30+
],
31+
"responses": {
32+
"200": {
33+
"description": "OK",
34+
"content": {
35+
"application/x-ndjson": {
36+
"schema": {
37+
"type": "array",
38+
"items": {
39+
"type": "string"
40+
}
41+
}
42+
},
43+
"application/json": {
44+
"schema": {
45+
"type": "array",
46+
"items": {
47+
"$ref": "#/components/schemas/Feature"
48+
}
49+
}
50+
}
51+
}
52+
}
53+
}
54+
}
55+
}
56+
},
57+
"components": {
58+
"schemas": {
59+
"Feature": {
60+
"type": "object",
61+
"properties": {
62+
"name": {
63+
"type": "string"
64+
}
65+
}
66+
}
67+
}
68+
}
69+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /