SpringBoot如何⽣成接⼝⽂档,⽼鸟们都这么玩的!
为什么要⽤Swagger ?
以下⽂章来源于JAVA⽇知录,作者飘渺Jam“作为⼀名程序员,我们最讨厌两件事:1. 别⼈不写注释。2. ⾃⼰写注释。⽽作为⼀名接⼝开发者,我们同样讨厌两件事:1. 别⼈不写接⼝⽂档,⽂档不及时更新。2. 需要⾃⼰写接⼝⽂档,还需要及时更新。”
相信⽆论是前端还是后端开发,都或多或少地被接⼝⽂档折磨过。前端经常抱怨后端给的接⼝⽂档与实际情况不⼀致。后端⼜觉得编写及维护接⼝⽂档会耗费不少精⼒,经常来不及更新。⽽随着Springboot、Springcloud等微服务的流⾏,每个项⽬都有成百上千个接⼝调⽤,这时候再要求⼈⼯编写接⼝⽂档并且保证⽂档的实时更新⼏乎是⼀件不可能完成的事,所以这时候我们迫切需要⼀个⼯具,⼀个能帮我们⾃动化⽣成接⼝⽂档以及⾃动更新⽂档的⼯具。它就是Swagger。Swagger 提供了⼀个全新的维护 API ⽂档的⽅式,有4⼤优点:
1. ⾃动⽣成⽂档:只需要少量的注解,Swagger 就可以根据代码⾃动⽣成 API ⽂档,很好的保证了⽂档的时效性。
2. 跨语⾔性,⽀持 40 多种语⾔。
3. Swagger UI 呈现出来的是⼀份可交互式的 API ⽂档,我们可以直接在⽂档页⾯尝试 API 的调⽤,省去了准备复杂的调⽤参数的过程。
4. 还可以将⽂档规范导⼊相关的⼯具(例如 SoapUI), 这些⼯具将会为我们⾃动地创建⾃动化测试。
现在我们知道了Swagger的作⽤,接下来将其集成到我们项⽬中。
Swagger集成
集成Swagger很简单,只需要简单三步。
第⼀步:引⼊依赖包
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--swagger-ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
第⼆步:修改配置⽂件
1. application.properties 加⼊配置
# ⽤于控制是否开启Swagger,⽣产环境记得关闭Swagger,将值设置为 false
abled = true
2.增加⼀个swagger配置类
@Configuration
@EnableSwagger2
@ConditionalOnClass(Docket.class)
public class SwaggerConfig {
private static final String VERSION = "1.0";
@Value("${abled}")
private Boolean swaggerEnabled;
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.
enable(swaggerEnabled)
.groupName("SwaggerDemo")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
.paths(PathSelectors.any())
.build();
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("接⼝⽂档")
.contact(new Contact("JAVA⽇知录","javadaily","jianzh5@163"))
.description("Swagger接⼝⽂档")
.version(VERSION)
.build();
}
}
这⾥通过.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))标明给加上@Api注解的类⾃动⽣成接⼝⽂档。
第三步,配置API接⼝
@RestController
@Api(tags = "参数校验")
@Slf4j
public class ValidController {
@PostMapping("/valid/test1")
@ApiOperation("RequestBody校验")
public String test1(@Validated @RequestBody ValidVO validVO){
log.info("validEntity is {}", validVO);
return "test1 valid success";
}
@ApiOperation("Form校验")
@PostMapping(value = "/valid/test2")
public String test2(@Validated ValidVO validVO){
log.info("validEntity is {}", validVO);
return "test2 valid success";
}
@ApiOperation("单参数校验")
@PostMapping(value = "/valid/test3")
public String test3(@Email String email){
log.info("email is {}", email);
return "email valid success";
}
}
通过@Api注解标注需要⽣成接⼝⽂档,通过@ApiOperation注解标注接⼝名。同时我们给ValidVO也加上对应的注解
@Data
@ApiModel(value = "参数校验类")
public class ValidVO {
@ApiModelProperty("ID")
private String id;
@ApiModelProperty(value = "应⽤ID",example = "cloud")
private String appId;
@NotEmpty(message = "级别不能为空")
@ApiModelProperty(value = "级别")
private String level;
@ApiModelProperty(value = "年龄")
private int age;
...
}
通过@ApiModel标注这是⼀个参数实体,通过@ApiModelProperty标注字段说明。
Unable to infer base url
@RestControllerAdvice(basePackages = "com.jianzh5.blog")
@Slf4j
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
...
}
通过添加basePackage属性限定统⼀返回值的范围,这样就不影响Swagger了。重启服务器再次访问swagger接⼝地址,就可以看到接⼝⽂档页⾯了。For input string: ""
Swagger2.9.2有个bug,就是当我们参数实体有int类型的参数时,打开Swagger接⼝页⾯时后端会⼀直提⽰异常:
java.lang.NumberFormatException: For input string: ""
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.base/java.lang.Long.parseLong(Long.java:702)
at java.base/java.lang.Long.valueOf(Long.java:1144)
有两种解决⽅案:
1. 给int类型的字段使⽤@ApiModelPorperty注解时添加example属性
@ApiModelProperty(value = "年龄",example = "10")
private int age;
2. 去除原swagger中的swagger-models和swagger-annotations,⾃⾏引⼊⾼版本的annotations和models
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.22</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.22</version>
</dependency>
集成Swagger过程中虽然会出现两个⼩问题,解决后我们就可以愉快享受Swagger给我们带来的便利了。
Swagger美化
Swagger原⽣UI有点丑,我们可以借助Swagger的增强⼯具knife4j优化⼀下。
第⼀步:引⼊依赖包
<!--整合Knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
“由于knife4j中已经带了swagger-annotations和swagger-models的依赖,所以我们可以把上⽂中⼿动添加的两个依赖删除。”
第⼆步:启⽤knife4j增强
@Configuration
@EnableSwagger2
@ConditionalOnClass(Docket.class)
@EnableKnife4j
public class SwaggerConfig {
...
}
Swagger参数分组
看到这⾥的同学⼼理肯定会想,就这?这就是⽼鸟的做法?跟我们新⼿也没啥区别呀!别急,我们先来看⼀个效果。⾸先我们定义了两个接⼝,⼀个新增,⼀个编辑
@ApiOperation("新增")
@PostMapping(value = "/valid/add")
public String add(@Validated(value = {ValidGroup.Crud.Create.class}) ValidVO validVO){
log.info("validEntity is {}", validVO);
return "test3 valid success";
}
@ApiOperation("更新")
@PostMapping(value = "/valid/update")
public String update(@Validated(value = ValidGroup.Crud.Update.class) ValidVO validVO){
log.info("validEntity is {}", validVO);
return "test4 valid success";
}
注意看,这⾥⽤的是同⼀个实体ValidVO来接收前端参数,只不过使⽤了参数校验中的分组,然后我们打开kife4j页⾯观察两者的接⼝⽂档有何不同。新增:编辑:通过上⾯可以看到,虽然⽤于接受参数的实体⼀样,但是当分组不⼀样时展⽰给前端的参数也不⼀样,这就是Swagger的分组功能。
当然原⽣的Swagger是不⽀持分组功能的,我们需要对Swagger进⾏扩展。我已经将代码上传到了github上,由于代码量⽐较多这⾥就不展⽰了,⼤家可以⾃⾏查阅。引⼊扩展类后还需要在Swagger配置类SwaggerConfig中注⼊对应的Bean。
@Configuration
springboot是啥@EnableSwagger2
@ConditionalOnClass(Docket.class)
@EnableKnife4j
public class SwaggerConfig {
...
@Bean
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public GroupOperationModelsProviderPlugin groupOperationModelsProviderPlugin() {
return new GroupOperationModelsProviderPlugin();
}
@Bean
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public GroupModelBuilderPlugin groupModelBuilderPlugin() {
return new GroupModelBuilderPlugin();
}
@Bean
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public GroupModelPropertyBuilderPlugin groupModelPropertyBuilderPlugin() {
return new GroupModelPropertyBuilderPlugin();
}
@Bean
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public GroupExpandedParameterBuilderPlugin groupExpandedParameterBuilderPlugin() {
return new GroupExpandedParameterBuilderPlugin();
}
@Bean
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public GroupOperationBuilderPlugin groupOperationBuilderPlugin() {
return new GroupOperationBuilderPlugin();
}
@Bean
@Primary
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public GroupModelAttributeParameterExpander groupModelAttributeParameterExpander(FieldProvider fields, AccessorsProvider accessors, EnumTypeDeterminer enumTypeDeterminer) {        return new GroupModelAttributeParameterExpan
der(fields, accessors, enumTypeDeterminer);
}
}
分组使⽤说明
1.在bean对象的属性⾥配置如下注释
@Null(groups = ValidGroup.Crud.Create.class)
@NotNull(groups = ValidGroup.Crud.Update.class,message = "应⽤ID不能为空")
@ApiModelProperty(value = "应⽤ID",example = "cloud")
private String appId;
当新增场景的时候,appId为空,不需要传值;当修改场景的时候,appId不能为空,需要传值;其他没有配置组的皆为默认组(Default)2.在接⼝参数的时候加⼊组规则校验
@ApiOperation("新增")
@PostMapping(value = "/valid/add")
public String add(@Validated(value = {ValidGroup.Crud.Create.class}) ValidVO validVO){
log.info("validEntity is {}", validVO);
return "test3 valid success";
}
当前接⼝会针对默认组的bean属性进⾏校验,同时针对保存常见的属性进⾏校验。
⼩结
Swagger集成相对来说还是很简单的,虽然在集成过程中也出现了⼏个⼩问题,不过也很容易就解决了。今天⽂章的重点内容是Swagger分组功能,跟之前的参数校验⽂章⼀
样,很多同学遇到这种分组场景时往往会选择创建多个实体类,虽然也能解决问题,只不过总是有点别扭。不过遗憾的是,本⽂中Swagger的分组扩展只⽀持Swagger2,⾄于
新版本Swagger3就不怎么友好了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。