Swagger枚举类型

9/7/2021

# 背景

经常后端会返回给前端一个枚举定义,比如性别:gender

gender = 1;// 表示男性
gender = 2;// 表示女性
1
2

# 问题

在给javabean添加swagger @ApiModelProperty注解的时候,就需要额外说明,每个值代表的是什么含义,如下:

public class PersonDTO {

    @ApiModelProperty("主键")
    private Long id;

    @ApiModelProperty("性别: 1男,2女")
    private Integer gender;
}
1
2
3
4
5
6
7
8

这样一旦再多加一种性别: gender = 3;// 表示中性

你就需要找到所有地方,都改下,但凡漏掉一个地方,就可能导致前端的对应不上,这种隐藏的Bug是十分危险的。

# 解决方案

# 定义枚举,实现BaseEnum接口
public interface BaseEnum {

    /**
     * 获取枚举类的值
     *
     * @return Object
     */
    Object getValue();

    /**
     * 获取枚举类的说明
     *
     * @return String
     */
    String getDesc();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

比如文件类的枚举变量:

public enum FileServiceTypeEnum implements BaseEnum {

    /**
     * 本地文件服务
     */
    LOCAL(1, FileServiceNameConst.LOCAL, "本地文件服务"),

    /**
     * 阿里OSS文件服务
     */
    ALI_OSS(2, FileServiceNameConst.ALI_OSS, "阿里OSS文件服务"),

    /**
     * 七牛文件服务
     */
    QI_NIU_OSS(3, FileServiceNameConst.QI_NIU_OSS, "七牛文件服务");

    private Integer locationType;
    private String serviceName;
    private String desc;

    FileServiceTypeEnum(Integer locationType, String serviceName, String desc) {
        this.locationType = locationType;
        this.serviceName = serviceName;
        this.desc = desc;
    }

    @Override
    public Integer getValue() {
        return this.locationType;
    }

    @Override
    public String getDesc() {
        return this.desc;
    }

    public String getServiceName() {
        return serviceName;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 为JavaBean 添加@ApiModelPropertyEnum注解
public class FileDTO {

    @ApiModelPropertyEnum(FileServiceTypeEnum.class)
    private Integer fileLocationType;

    @ApiModelProperty("文件名称")
    private String fileName;
1
2
3
4
5
6
7
# ApiModelPropertyEnum注解

我们自己实现了一个swagger插件ModelPropertyBuilderPlugin插件:SmartSwaggerApiModelEnumPlugin,在插件中使用@ApiModelPropertyEnum注解,这样在swagger文档中就可以很好的显示了

public class SmartSwaggerApiModelEnumPlugin implements ModelPropertyBuilderPlugin {

    @Override
    public void apply(ModelPropertyContext context) {
        Optional<ApiModelPropertyEnum> annotation = Optional.absent();

        if (context.getAnnotatedElement().isPresent()) {
            annotation = annotation.or(findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
        }
        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = annotation.or(findPropertyAnnotation(context.getBeanPropertyDefinition().get(), ApiModelPropertyEnum.class));
        }

        if (annotation.isPresent()) {
            Class<? extends BaseEnum> aClass = annotation.get().value();
            String enumInfo = BaseEnum.getInfo(aClass);
            String enumDesc = annotation.get().enumDesc();
            context.getBuilder().required(annotation.transform(toIsRequired()).or(false))
                .description(enumDesc +":"+enumInfo)
                .example(annotation.transform(toExample()).orNull());
        }
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }

    static Function<ApiModelPropertyEnum, Boolean> toIsRequired() {
        return annotation -> annotation.required();
    }

    public static Optional<ApiModelPropertyEnum> findApiModePropertyAnnotation(AnnotatedElement annotated) {
        return Optional.fromNullable(AnnotationUtils.getAnnotation(annotated, ApiModelPropertyEnum.class));
    }

    static Function<ApiModelPropertyEnum, String> toExample() {
        return annotation -> {
            String example = annotation.example();
            if (StringUtils.isBlank(example)) {
                return "";
            }
            return example;
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# swagger显示效果

效果如下: image.png

你会发现这不是前端代码吗?为什么要这样?

# 为了前端使用vue-enum

因为swagger文档多用于前端,后端人员较少,前端使用的枚举维护是:vue-enum (opens new window)

所以直接用Java代码生成了前端代码,这样前端人员直接copy走就可以了。

关爱前端,节省时间。

当然如果前端是app端的话,想修改返回结果,可以直接修改 BaseEnum.getInfo() 方法


作者简介: 卓大 (opens new window), 1024创新实验室主任,混迹于各个技术圈,熟悉点java,略懂点前端。

加“卓大”微信,入群 关注 1024创新实验室! 我要请 1024创新实验室 喝胡辣汤~