swagger-api / swagger-api/swagger-codegen-generators
[Spring] Incorrect code generated for endpoints with multiple request types
Nobody has claimed this yet.
- Dominant language
- Mustache
- Stars
- 299
- Forks
- 439
- PR merge metrics
- No merged PRs in 30d
Description
Description
The swagger code generator generates ambiguous code which compiles but does not run for endpoints with multiple content types. Take this example where the request body content type can be 'application/json' or 'multipart/form-data':
...
put:
summary: "Update a rule set with the supplied rule definitions."
operationId: "updateRuleSetWithRules"
parameters:
- name: "id"
in: "path"
required: true
style: "simple"
explode: false
schema:
type: "integer"
format: "int32"
requestBody:
description: "rules set with rules to be stored"
content:
application/json:
schema:
$ref: "#/components/schemas/RuleSetWithRulesVM"
multipart/form-data:
schema:
type: "object"
properties:
ruleSetCsvFile:
type: "string"
description: "The CSV file containing the rules."
format: "binary"
required: true
responses:
200:
...
The API file that gets generated contains the following to methods which the Java compiler can successfully compile:
...
@RequestMapping(value = "/ruleSets/{id}/rules",
produces = { "application/json" },
consumes = { "application/json", "multipart/form-data" },
method = RequestMethod.PUT)
default ResponseEntity<RuleSetWithRulesVM> updateRuleSetWithRules(@ApiParam(value = "rules set with rules to be stored" ,required=true ) @Valid @RequestBody RuleSetWithRulesVM body,@ApiParam(value = "",required=true) @PathVariable("id") Integer id) {
return getDelegate().updateRuleSetWithRules(body, id);
}
...
and
@RequestMapping(value = "/ruleSets/{id}/rules",
produces = { "application/json" },
consumes = { "application/json", "multipart/form-data" },
method = RequestMethod.PUT)
default ResponseEntity<RuleSetWithRulesVM> updateRuleSetWithRules(@ApiParam(value = "file detail") @Valid @RequestPart("file") MultipartFile ruleSetCsvFile,@ApiParam(value = "",required=true) @PathVariable("id") Integer id) {
return getDelegate().updateRuleSetWithRules(ruleSetCsvFile, id);
}
The problem arises when you start the resulting application because Spring is unable to distinguish between the two overloaded methods as they are missing the params option from the @RequestMapping annotation.
Swagger-codegen version
3.011-SNAPSHOT
Swagger declaration file content or url
/ruleSets/{id}/rules:
put:
tags:
- "rules"
summary: "Update a rule set with the supplied rule definitions."
operationId: "updateRuleSetWithRules"
parameters:
- name: "id"
in: "path"
required: true
style: "simple"
explode: false
schema:
type: "integer"
format: "int32"
requestBody:
description: "rules set with rules to be stored"
content:
application/json:
schema:
$ref: "#/components/schemas/RuleSetWithRulesVM"
multipart/form-data:
schema:
type: "object"
properties:
ruleSetCsvFile:
type: "string"
description: "The CSV file containing the rules."
format: "binary"
required: true
responses:
200:
description: "success"
content:
application/json:
schema:
$ref: "#/components/schemas/RuleSetWithRulesVM"
Command line used for generation
The code is generated using Maven with the following configuration:
<configuration>
<ignoreFileOverride>${project.basedir}/src/main/resources/.swagger-codegen-ignore</ignoreFileOverride>
<inputSpec>${project.basedir}/src/main/resources/public/swagger-ml-classification-1.0.0.yaml</inputSpec>
<language>spring</language>
<configOptions>
<sourceFolder>src/main/java</sourceFolder>
<apiPackage>${default-server-package}.api</apiPackage>
<configPackage>${default-server-package}.configuration</configPackage>
<invokerPackage>${default-server-package}</invokerPackage>
<modelPackage>${default-server-package}.model</modelPackage>
<delegatePattern>true</delegatePattern>
<java8>true</java8>
<dateLibrary>java8</dateLibrary>
</configOptions>
</configuration>
Steps to reproduce
- Create an endpoint with two content types in the request body as shown above
- Generate the code for Spring Boot
- Run the resulting application to see the Spring startup errors
Related issues/PRs
None found
Suggest a fix/enhancement
To fix the problem, the code generator should generate code that includes the params option in the @RequestMapping annotation as shown in this example:
...
@RequestMapping(value = "/ruleSets/{id}/rules",
produces = { "application/json" },
consumes = { "application/json", "multipart/form-data" },
method = RequestMethod.PUT, params = {"body", "id"})
default ResponseEntity<RuleSetWithRulesVM> updateRuleSetWithRules(@ApiParam(value = "rules set with rules to be stored" ,required=true ) @Valid @RequestBody RuleSetWithRulesVM body,@ApiParam(value = "",required=true) @PathVariable("id") Integer id) {
return getDelegate().updateRuleSetWithRules(body, id);
}
...
@RequestMapping(value = "/ruleSets/{id}/rules",
produces = { "application/json" },
consumes = { "application/json", "multipart/form-data" },
method = RequestMethod.PUT, params = {"ruleSetCsvFile", "id"})
default ResponseEntity<RuleSetWithRulesVM> updateRuleSetWithRules(@ApiParam(value = "file detail") @Valid @RequestPart("file") MultipartFile ruleSetCsvFile,@ApiParam(value = "",required=true) @PathVariable("id") Integer id) {
return getDelegate().updateRuleSetWithRules(ruleSetCsvFile, id);
}
...
Please note the following changes in the suggested solution:
method = RequestMethod.PUT, params = {"body", "id"})
method = RequestMethod.PUT, params = {"ruleSetCsvFile", "id"})
Instead of the original:
method = RequestMethod.PUT)
method = RequestMethod.PUT)
The following patch could a be way of possibly fixing the issue but it will need work further work the params code is only generated under the right conditions:
diff --git a/src/main/resources/handlebars/JavaSpring/api.mustache b/src/main/resources/handlebars/JavaSpring/api.mustache
index 7551be6..bed2f10 100644
--- a/src/main/resources/handlebars/JavaSpring/api.mustache
+++ b/src/main/resources/handlebars/JavaSpring/api.mustache
@@ -101,7 +101,8 @@ public interface {{classname}} {
consumes = "{{{vendorExtensions.x-contentType}}}",{{/hasConsumes}}{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}}
produces = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }, {{/hasProduces}}{{#hasConsumes}}
consumes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} },{{/hasConsumes}}{{/singleContentTypes}}
- method = RequestMethod.{{httpMethod}})
+ method = RequestMethod.{{httpMethod}},
+ params = { {{#parameters}}"{{paramName}}"{{#hasMore}}, {{/hasMore}}{{/parameters}} })
{{#jdk8}}default {{/jdk8}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{#delegate-method}}_{{/delegate-method}}{{operationId}}({{#parameters}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/parameters}}){{^jdk8}};{{/jdk8}}{{#jdk8}} {
{{#delegate-method}}
return {{operationId}}({{#parameters}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/parameters}});
@@ -135,4 +136,4 @@ public interface {{classname}} {
{{/contents}}
{{/operation}}
}
-{{/operations}}
\ No newline at end of file
+{{/operations}}
diff --git a/src/main/resources/mustache/JavaSpring/api.mustache b/src/main/resources/mustache/JavaSpring/api.mustache
index c03be38..bed2f10 100644
--- a/src/main/resources/mustache/JavaSpring/api.mustache
+++ b/src/main/resources/mustache/JavaSpring/api.mustache
@@ -101,8 +101,8 @@ public interface {{classname}} {
consumes = "{{{vendorExtensions.x-contentType}}}",{{/hasConsumes}}{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}}
produces = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }, {{/hasProduces}}{{#hasConsumes}}
consumes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} },{{/hasConsumes}}{{/singleContentTypes}}
- method = RequestMethod.{{httpMethod}}
- params = { {{#parameters}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/parameters}} })
:
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/main/resources/handlebars/JavaSpring/api.mustache and src/main/resources/mustache/JavaSpring/api.mustache, then reproduce the Spring Boot generation with an endpoint containing both request content types. Check the generated @RequestMapping annotations and confirm the resulting application starts without ambiguous mappings; ensure the parameter conditions work for both generated methods.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100