SpringBoot 如何生成接口文档,老鸟们都这么玩的!
为什么要用Swagger ?
“ 作为一名程序员,我们最讨厌两件事:1. 别人不写注释。2. 自己写注释。 而作为一名接口开发者,我们同样讨厌两件事:1. 别人不写接口文档,文档不及时更新。2. 需要自己写接口文档,还需要及时更新。 ”
自动生成文档:只需要少量的注解,Swagger 就可以根据代码自动生成 API 文档,很好的保证了文档的时效性。 跨语言性,支持 40 多种语言。 Swagger UI 呈现出来的是一份可交互式的 API 文档,我们可以直接在文档页面尝试 API 的调用,省去了准备复杂的调用参数的过程。 还可以将文档规范导入相关的工具(例如 SoapUI), 这些工具将会为我们自动地创建自动化测试。
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>
第二步:修改配置文件
application.properties 加入配置
# 用于控制是否开启Swagger,生产环境记得关闭Swagger,将值设置为 false
springfox.swagger2.enabled = true
@Configuration
@EnableSwagger2
@ConditionalOnClass(Docket.class)
public class SwaggerConfig {
private static final String VERSION = "1.0";
@Value("${springfox.swagger2.enabled}")
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日知录","http://javadaily.cn","jianzh5@163.com"))
.description("Swagger接口文档")
.version(VERSION)
.build();
}
}
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
标明给加上@Api
注解的类自动生成接口文档。第三步,配置API接口
@RestController
@Api(tags = "参数校验")
@Slf4j
@Validated
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
http://localhost:8080/swagger-ui.html
体验一下。ResponseBodyAdvice
统一处理返回值/响应体,导致给Swagger的返回值也包装了一层,UI页面无法解析。可以通过http://localhost:8080/v2/api-docs?group=SwaggerDemo
观察Swagger返回的json数据。@RestControllerAdvice(basePackages = "com.jianzh5.blog")
@Slf4j
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
...
}
For input string: ""
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)
给int类型的字段使用 @ApiModelPorperty
注解时添加example属性
@ApiModelProperty(value = "年龄",example = "10")
private int age;
去除原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>
<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美化
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 {
...
}
http://localhost:8080/doc.html
即可看到效果。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页面观察两者的接口文档有何不同。@Configuration
@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 GroupModelAttributeParameterExpander(fields, accessors, enumTypeDeterminer);
}
}
分组使用说明
@Null(groups = ValidGroup.Crud.Create.class)
@NotNull(groups = ValidGroup.Crud.Update.class,message = "应用ID不能为空")
@ApiModelProperty(value = "应用ID",example = "cloud")
private String appId;
@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";
}
小结
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️