ToB企服应用市场:ToB评测及商务社交产业平台
标题:
JAVA中自定义扩展Swagger的能力,自动生成参数取值含义说明,提升开发效率
[打印本页]
作者:
忿忿的泥巴坨
时间:
2022-9-17 08:35
标题:
JAVA中自定义扩展Swagger的能力,自动生成参数取值含义说明,提升开发效率
大家好,又见面了。
在JAVA做前后端分离的项目开发的时候,服务端需要提供接口文档供周边人员做接口的对接指导。越来越多的项目都在尝试使用一些基于代码自动生成接口文档的工具来
替代由开发人员手动编写接口文档
,而Swagger作为一款优秀的在线接口文档生成工具,以其功能强大、集成方便而得到了广泛的使用。
在项目中有一种非常常见的场景,就是接口的请求或者响应参数中会有一些字段的
取值会限定为固定的几个可选值之一
,而在代码中这些可选值往往会通过定义
枚举类
的方式来承载,比如:
根据操作类型,过滤对应类型的用户操作日志列表
如:
http://127.0.0.1:8088/test/queryOperateLogs?operateType=2
这里的请求参数operateType传入的值需要在后端约定的取值范围内,这个取值范围的定义如下:
@Getter
@AllArgsConstructor
public enum OperateType {
ADD(1, "新增或者创建操作"),
MODIFY(2, "更新已有数据操作"),
DELETE(3, "删除数据操作"),
QUERY(4, "查询数据操作");
private int value;
private String desc;
}
复制代码
这里就需要我们在接口文档里面将此接口中operateType的可选值以及每个可选值对应的含义信息都说明清楚,这样调用方在使用的时候才知道应该传入什么值。
我们基于Swagger提供的基础注解能力来实现时,比较常见的会看到如下两种写法:
写法1
:
接口定义的时候,指定入参的取值说明
接口URL中携带的请求入参信息,通过@ApiImplicitParam注解来告诉调用方此接口允许接收的合法operateType的取值范围以及各个取值的含义。
比如下面这种场景:
@GetMapping("/queryOperateLogs")
@ApiOperation("查询指定操作类型的操作日志列表")
@ApiImplicitParam(name = "operateType", value = "操作类型,取值说明: 1,新增;2,更新;3,除;4,查询", dataType = "int", paramType = "query")
public List<OperateLog> queryOperateLogs(int operateType) {
return testService.queryOperateLogs(operateType);
}
复制代码
这样,在swagger界面上就可以显示出字段的取值说明信息。
其实还有一种写法,即在代码的入参前面添加@ApiParam注解的方式来实现。比如:
@GetMapping("/queryOperateLogs")
@ApiOperation("查询指定操作类型的操作日志列表")
public List<OperateLog> queryOperateLogs(@ApiParam(value = "操作类型,取值说明: 1,新增;2,更新;3,删除;4,查询") @RequestParam("type") int operateType) {
return testService.queryOperateLogs(operateType);
}
复制代码
这样也能达到相同的效果。
写法2
:
请求或者响应的Body体中解释字段的取值说明
对于需要使用json体进行传输的请求或者响应
消息体Model中
,可以使用@ApiModelProperty添加含义说明。
@Data
@ApiModel("操作记录信息")
public class OperateLog {
@ApiModelProperty("操作类型,取值说明: 1,新增;2,更新;3,删除;4,查询")
private int operateType;
@ApiModelProperty("操作用户")
private String user;
@ApiModelProperty("操作详情")
private String detail;
}
复制代码
同样,在Swagger界面就可以清楚的知道每个字段的具体含义与取值说明。
但是上面的两个写法,都
存在着同一个问题
,就是如果枚举类中的值内容含义有变更,比如OperateType枚举类中新增了一个BATCH_DELETE(5, "批量删除"), 则必须手动去修改所有涉及的接口上的Swagger描述信息。如果有大量场景都涉及此字段,则
要改动的地方就非常多,且极易漏掉
(因为不好通过代码关联关系直接搜索到)。这样对于开发人员维护起来的成本就会增加,久而久之会导致接口文档的内容与实际代码处理情况不相匹配。
那么,有没有什么简单的方式,可以让接口文档自动根据对应枚举类的内容变更而动态变更呢?
Swagger没有提供原生的此方面能力支持,但是我们可以通过一些简单的方式对Swagger的能力进行扩展,让Swagger支持我们的这种诉求。一起来看下如何实现吧。
扩展可行性分析
既然想要改变生成的Swagger文档中指定字段的描述内容,那么首先就应该是要搞清楚Swagger中现在的内容生成逻辑是如何处理的。我们以@ApiParam为例进行分析。因为@ApiParam中指定的内容会被显示到Swagger界面上,那么在Swagger的框架中,一定有个地方会尝试去获取此注解中指定的相关字段值,然后将注解的内容转为界面上的文档内容。所以
想要定制,首先必须要了解当前是如何处理
的。
翻看Swagger的源码,发现在ApiParamParameterBuilder类中进行此部分逻辑的处理,处理逻辑如下:
看了下此类是ParameterBuilderPlugin接口的一个实现类,Swagger框架在遍历并逐个生成parameter说明信息的时候会被调用此实现类的逻辑来执行。
到这里其实问题就已经很明显了,我们可以自定义一个处理类并实现ParameterBuilderPlugin接口,然后将我们的诉求在自定义的处理类中进行实现,这样不就可以实现我们的诉求了吗?
相同的策略,我们可以找到处理@ApiImplicitParam、@ApiModelProperty对应的接口类。
根据上面的分析,我们
只需要提供个自定义实现类,然后分别实现这几个接口就可以搞定我们的诉求
了。那应该如何进行封装,将其作为一个通用能力供所有场景使用呢,下面详细讨论下。
自定义注解实现基于枚举类生成描述
前面已经找到了一种思路将我们的定制逻辑注入到Swagger的文档生成框架中进行调用,那么下一步我们就得确认一种相对简单的策略,告诉框架哪个字段需要使用枚举来自动生成取值说明,以及使用哪个枚举类来生成。
这里我们使用
自定义注解
的方式来实现。Swagger为不同的场景分别提供了@APIParam、@ApiImplicitParam、@ApiModelProperty等不同的注解,我们可以简化下,提供一个统一的自定义注解即可。
比如:
[code]@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })@Retention(RetentionPolicy.RUNTIME)public @interface ApiPropertyReference { // 接口文档上的显示的字段名称,不设置则使用field本来名称 String name() default ""; // 字段简要描述,可选 String value() default ""; // 标识字段是否必填 boolean required() default false; // 指定取值对应的枚举类 Class
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4