Spring Boot中使用继承时Swagger出现重复的模型类型问题。

huangapple go评论69阅读模式
英文:

Spring Boot Swagger duplicate model type when using inheritance

问题

我有一个使用Spring Boot和Swagger的Java项目。该项目包含一个简单的API控制器,实现了一个单一的端点。在端点上调用GET方法会获取所有用户。在同一端点上调用PUT方法会更新单个用户:

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    @RequestMapping(value = "", method = RequestMethod.GET)
    public List<BaseUser> getUsers(String name) {
        return null;
    }

    @RequestMapping(value = "", method = RequestMethod.PUT)
    public BaseUser updateUser(@RequestBody BaseUser user) {
        return null;
    }
}

/api/v1/users 端点是使用 BaseUser 实现的,它有诸如 EmployeeUser 的子类型:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = EmployeeUser.class, name = "employee")
})
public abstract class BaseUser {

    private final int id;
    private final String name;

    @JsonCreator
    protected BaseUser(
            @JsonProperty("id") int id,
            @JsonProperty("name") String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}
public class EmployeeUser extends BaseUser {
    public EmployeeUser(@JsonProperty("id") int id,
                     @JsonProperty("name") String name) {
        super(id, name);
    }
}

根据这个结构,在浏览Swagger UI时,我看到了这些模型类型的重复项(基于OpenAPI规范中的定义)。

Spring Boot中使用继承时Swagger出现重复的模型类型问题。

我期望只看到一个BaseUser和一个EmployeeUser。重复出现的原因是什么?

OpenAPI规范中的定义如下:

"definitions": {
    "BaseUserReq": {
        "type": "object",
        "discriminator": "type",
        "properties": {
            "id": {
                "type": "integer",
                "format": "int32"
            },
            "name": {
                "type": "string"
            }
        },
        "title": "BaseUserReq"
    },
    "BaseUserRes": {
        "type": "object",
        "discriminator": "type",
        "properties": {
            "id": {
                "type": "integer",
                "format": "int32"
            },
            "name": {
                "type": "string"
            }
        },
        "title": "BaseUserRes"
    },
    "EmployeeUserReq": {
        "title": "EmployeeUser",
        "allOf": [{
            "$ref": "#/definitions/BaseUserReq"
        }, {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32"
                },
                "name": {
                    "type": "string"
                }
            },
            "title": "EmployeeUserReq"
        }]
    },
    "EmployeeUserRes": {
        "title": "EmployeeUser",
        "allOf": [{
            "$ref": "#/definitions/BaseUserRes"
        }, {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32"
                },
                "name": {
                    "type": "string"
                }
            },
            "title": "EmployeeUserRes"
        }]
    }
}
英文:

I've a Java project that uses Spring Boot and Swagger. The project consists of a simple API controller that implements a single endpoint. Invoking the GET method on the endpoint gets all users. Invoking the PUT method on the same endpoint updates a single user:

@RestController
@RequestMapping(&quot;/api/v1/users&quot;)
public class UserController {
    @RequestMapping(value = &quot;&quot;, method = RequestMethod.GET)
    public List&lt;BaseUser&gt; getUsers(String name) {
        return null;
    }

    @RequestMapping(value = &quot;&quot;, method = RequestMethod.PUT)
    public BaseUser updateUser(@RequestBody BaseUser user) {
        return null;
    }
}

The /api/v1/users endpoint is implemented using a BaseUser which has subtypes like EmployeeUser:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = &quot;type&quot;)
@JsonSubTypes({
        @JsonSubTypes.Type(value = EmployeeUser.class, name = &quot;employee&quot;)
})
public abstract class BaseUser {

    private final int id;
    private final String name;

    @JsonCreator
    protected BaseUser(
            @JsonProperty(&quot;id&quot;) int id,
            @JsonProperty(&quot;name&quot;) String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}
public class EmployeeUser extends BaseUser {
    public EmployeeUser(@JsonProperty(&quot;id&quot;) int id,
                     @JsonProperty(&quot;name&quot;) String name) {
        super(id, name);
    }
}

With this structure, when I browse the Swagger UI I see duplicates of these model types (based on the definitions in the OpenAPI spec).

Spring Boot中使用继承时Swagger出现重复的模型类型问题。

I expected to see only a single BaseUser and a single EmployeeUser. What is the reason for the duplicates?

Definitions from OpenAPI Spec:

&quot;definitions&quot;: {
    &quot;BaseUserReq&quot;: {
        &quot;type&quot;: &quot;object&quot;,
        &quot;discriminator&quot;: &quot;type&quot;,
        &quot;properties&quot;: {
            &quot;id&quot;: {
                &quot;type&quot;: &quot;integer&quot;,
                &quot;format&quot;: &quot;int32&quot;
            },
            &quot;name&quot;: {
                &quot;type&quot;: &quot;string&quot;
            }
        },
        &quot;title&quot;: &quot;BaseUserReq&quot;
    },
    &quot;BaseUserRes&quot;: {
        &quot;type&quot;: &quot;object&quot;,
        &quot;discriminator&quot;: &quot;type&quot;,
        &quot;properties&quot;: {
            &quot;id&quot;: {
                &quot;type&quot;: &quot;integer&quot;,
                &quot;format&quot;: &quot;int32&quot;
            },
            &quot;name&quot;: {
                &quot;type&quot;: &quot;string&quot;
            }
        },
        &quot;title&quot;: &quot;BaseUserRes&quot;
    },
    &quot;EmployeeUserReq&quot;: {
        &quot;title&quot;: &quot;EmployeeUser&quot;,
        &quot;allOf&quot;: [{
            &quot;$ref&quot;: &quot;#/definitions/BaseUserReq&quot;
        }, {
            &quot;type&quot;: &quot;object&quot;,
            &quot;properties&quot;: {
                &quot;id&quot;: {
                    &quot;type&quot;: &quot;integer&quot;,
                    &quot;format&quot;: &quot;int32&quot;
                },
                &quot;name&quot;: {
                    &quot;type&quot;: &quot;string&quot;
                }
            },
            &quot;title&quot;: &quot;EmployeeUserReq&quot;
        }]
    },
    &quot;EmployeeUserRes&quot;: {
        &quot;title&quot;: &quot;EmployeeUser&quot;,
        &quot;allOf&quot;: [{
            &quot;$ref&quot;: &quot;#/definitions/BaseUserRes&quot;
        }, {
            &quot;type&quot;: &quot;object&quot;,
            &quot;properties&quot;: {
                &quot;id&quot;: {
                    &quot;type&quot;: &quot;integer&quot;,
                    &quot;format&quot;: &quot;int32&quot;
                },
                &quot;name&quot;: {
                    &quot;type&quot;: &quot;string&quot;
                }
            },
            &quot;title&quot;: &quot;EmployeeUserRes&quot;
        }]
    }
}

答案1

得分: 2

我在版本3.0.0中遇到了类似的问题和其他问题,所以我仍然在我的项目中使用2.9.2版本。您可以尝试以下替代方法。

在测试项目中更改依赖项(移除springfox-bean-validatorsspringfox-boot-starter)为:

<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.9.2</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>

...并将SwaggerConfig(在更改依赖项后移除缺失的导入)更改为:

@Configuration
@EnableSwagger2
public class SwaggerConfig { }

...Swagger UI端点将位于http://localhost:8080/swagger-ui.html,并返回您期望的类:

Spring Boot中使用继承时Swagger出现重复的模型类型问题。


此外,您可以移除spring-core依赖,因为它已经包含在spring-boot-starter-web中,还可以移除maven-compiler-plugin依赖,因为它是一个插件,并且可以移除重复的Maven编译器源代码和目标定义,这些定义已经作为属性定义了一次,并且作为Maven编译器插件的配置定义了第二次。

英文:

I've had a similar and other problems with version 3.0.0., so I still use 2.9.2 in my projects. You can try the following as an alternative.

When changing dependencies (removed springfox-bean-validators, springfox-boot-starter) in your test project to:

&lt;dependency&gt;
   &lt;groupId&gt;io.springfox&lt;/groupId&gt;
   &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
   &lt;version&gt;2.9.2&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
   &lt;groupId&gt;io.springfox&lt;/groupId&gt;
   &lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
   &lt;version&gt;2.9.2&lt;/version&gt;
&lt;/dependency&gt;

... and changing SwaggerConfig (removing missing imports after changed dependencies) to:

@Configuration
@EnableSwagger2
public class SwaggerConfig { }

... the Swagger UI endpoint at http://localhost:8080/swagger-ui.html will return classes you expect:

Spring Boot中使用继承时Swagger出现重复的模型类型问题。


Also, you can remove spring-core dependecy since it is already packed within the spring-boot-starter-web, maven-compiler-plugin dependency since it is a plugin, and remove duplicated maven compiler source and target definitions that are defined as properties and a second time as a configuration of the maven compiler plugin.

答案2

得分: 2

BaseUserReq、BaseUserRes、EmployeeUserReq和EmployeeUserRes是由于Springfox 3.0默认使用Swagger v3模型而生成的。您可以在application.properties中禁用它。

> Springfox 3.0默认使用v3模型。为了暂时解决这个问题,
> 在application.properties中添加:springfox.documentation.swagger.v2.use-model-v3=false
> 来自https://github.com/springfox/springfox/issues/3503#issuecomment-684819445

在禁用了springfox v3模型后,我得到了:

Spring Boot中使用继承时Swagger出现重复的模型类型问题。

这个问题在这里有描述:像“_1”这样带有数字的后缀被添加到重复的对象名称

> 嗨,@pitzcarraldo。感谢您的报告。
>
> 最近合并到主分支的更改,已在3.0.0-SNAPSHOT版本中提供,其意图是区分具有非对称属性的模型。
> 因此,如果模型在序列化反序列化时具有不同的属性,SpringFox将生成两个模型。
> 用于序列化的“ModelName”和用于反序列化的“ModelName_1”,反之亦然。
> 来自https://github.com/springfox/springfox/issues/3014#issuecomment-497941639

我通过移除BaseUserEmployeeUser构造函数中的@JsonCreator@JsonProperty来解决了这个问题。

Spring Boot中使用继承时Swagger出现重复的模型类型问题。

截至springfox 3.0.0,Swagger URL为http://localhost:8080/swagger-ui/

请查看GitHub仓库https://github.com/ielatif/swagger-test

提交历史以获取解释https://github.com/ielatif/swagger-test/commits/master

英文:

BaseUserReq, BaseUserRes, EmployeeUserReq and EmployeeUserRes are generated because Springfox 3.0 uses Swagger v3 models by default. You can disable it in application.properties.

> Springfox 3.0 uses v3 models by default. To workaround it for now,
> add: springfox.documentation.swagger.v2.use-model-v3=false in your
> application.properties.
> from https://github.com/springfox/springfox/issues/3503#issuecomment-684819445

After disabling springfox v3 models I got :

Spring Boot中使用继承时Swagger出现重复的模型类型问题。

This issue is described here Suffix with number like "_1" is added to duplicated object name

> Hi, @pitzcarraldo. Thanks for the report.
>
> The intention for the last changes, that've been merged to master, and
> became available in the 3.0.0-SNAPSHOT version is to differ model with
> not symmetric properties. So if model has different properties for
> serialization and deserialization SpringFox will generate two models.
> "ModelName" for serialization and "ModelName_1" for deserialization or
> vise versa.
> from https://github.com/springfox/springfox/issues/3014#issuecomment-497941639

I fixed it by removing @JsonCreator and @JsonProperty in BaseUser and EmployeeUser constructors

Spring Boot中使用继承时Swagger出现重复的模型类型问题。

As of springfox 3.0.0 swagger url is http://localhost:8080/swagger-ui/

See github repository https://github.com/ielatif/swagger-test

Commits history for explanations https://github.com/ielatif/swagger-test/commits/master

答案3

得分: 0

继承对我有用,但是如果我在模型中添加任何额外的方法(或者也许只是以 setXXX、getXXX 和 isXXX 命名的方法),在我的项目中就会出现这种情况。

如果我将我的模型制作成简单的 Java Bean,只包含设置器和获取器,没有其他逻辑,它会生成一个没有 Res 或 Req 后缀的单一模式对象。

英文:

Inheritance works for me, but this happens in my project if I add any extra methods to the models (or maybe it's just methods named setXXX, getXXX and isXXX).

If I make my models simple Java beans with just setters and getters and no other logic, it makes a single schema object without Res or Req suffixes.

huangapple
  • 本文由 发表于 2020年9月25日 17:15:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/64061238.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定