Spring Boot接口文档自动生成最佳方案
背景
最近研发团队需要做有状态服务,需要复杂的yaml格式的数据抽象为对象,接口定义入参出参涉及的字段多,对象多,接口文档编写将是个麻烦事,尤其是前后端分离的开发模式下,维护好接口文档尤其重要。
手动维护接口文档,费时费力,而且接口可能改,前端拿到的文档可能已过期,可能造成许多不必要的麻烦,你可以想象你的前端同事想杀死你的表情,因此自动生成接口文档的方案尤为重要。
以下是几种方案选型,文章结尾给出了我个人认为的最佳实践。
Swagger-UI
官网链接地址:https://swagger.io
GitHub: https://github.com/swagger-api/swagger-ui
- 直接运行,可以在线测试 API 接口。
- 号称世界上最流行的 API 框架。
- RESTFul API 文档在线自动生成工具 API 文档与 API 定义同步更新。
- 支持多种语言 如:Java 、PHP、Asp.net……
简介
Swagger UI 允许任何人(无论您是开发团队还是最终用户)都可以可视化 API 资源并与之交互,而无需任何实现逻辑。它是根据您的 OpenAPI(以前称为 Swagger)规范自动生成的,具有可视化文档,可简化后端实现和客户端使用。
特点
- 无依赖 UI 可以在任何开发环境中使用,无论是本地还是在 Web 端中。
- 人性化 允许最终开发人员轻松地进行交互,并尝试 API 公开的每个操作,以方便使用。
- 易于浏览 归类整齐的文档可快速查找并使用资源和端点。
- 所有浏览器支持 Swagger UI 在所有主要浏览器中均可使用,以适应各种可能的情况。
- 完全可定制 通过完整的源代码访问方式以所需方式设置和调整 Swagger UI。
- 完整的 OAS 支持 可视化 Swagger 2.0 或 OAS 3.0 中定义的 API。
SpringBoot 集成 Swagger
POM 文件中导入 springfox-swagger2 和 springfox-swagger-ui
<dependency>
<groupId> io.springfox </groupId>
<artifactId> springfox-swagger2 </artifactId>
<version> 2.7.0 </version>
</dependency>
<dependency>
<groupId> io.springfox </groupId>
<artifactId> springfox-swagger-ui </artifactId>
<version> 2.7.0 </version>
</dependency>
- 配置 Swagger 编写 SwaggerConfig 文件,具体配置如下
@Configuration
public class SwaggerConfig {
@Bean
public Docket customDocket() {
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
tokenPar.name("token").description("user token")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false).build();
pars.add(tokenPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.build()
.globalOperationParameters(pars)
.apiInfo(apiInfo())
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("api swagger document")
.description("swagger api")
.build();
}
private List<ApiKey> securitySchemes() {
List<ApiKey> apiKeys = new ArrayList<>();
apiKeys.add(new ApiKey("Authorization", "token", "header"));
return apiKeys;
}
private List<SecurityContext> securityContexts() {
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$")).build());
return securityContexts;
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
}
- 启动类加注解@EnableSwagger2 开启 Swagger2
- @ApiParam @ApiModelProperty
总结
Swagger UI有两种风格,每个人使用习惯不同,根据具体喜好选择自己中意的风格。
Yapi
简介
YApi 是高效、易用、功能强大的 api 管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API,YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以及简单的点击操作就可以实现接口的管理。
特点
- 基于 Json5 和 Mockjs 定义接口返回数据的结构和文档,效率提升多倍
- 扁平化权限设计,即保证了大型企业级项目的管理,又保证了易用性
- 类似 postman 的接口调试
- 自动化测试, 支持对 Response 断言
- MockServer 除支持普通的随机 mock 外,还增加了 Mock 期望功能,根据设置的请求过滤规则,返回期望数据
- 支持 postman, har, swagger 数据导入
- 免费开源,内网部署,信息再也不怕泄露了
教程
代码增量实现
通过自动识别 Swagger 地址解析成 Yapi 可识别的入参或者出参
- 当前问题
- 返回的数据是 AjaxResult 继承了 HashMap<String, Object>,swagger 无法识别每个字段的含义?
- yapi 可识别的数据格式是什么?
- 解决
- 定义了返回类型 Result
@Component
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = -6322750099185092004L;
@ApiModelProperty(name = "status",value = "状态:y,n")
private String status;
@ApiModelProperty(name = "success",value = "是否成功")
private boolean success;
@ApiModelProperty(name = "msg",value = "提示信息")
private String msg;
@ApiModelProperty(name = "info",value = "错误信息")
private String info;
@ApiModelProperty(name = "data",value = "返回对象")
private T data;
private static final String STATUS_Y = "y";
private static final String STATUS_N = "n";
public Result() {
}
public Result(boolean success, String msg, T data) {
this.success = success;
this.msg = msg;
this.data = data;
}
public Result(boolean success, String status) {
this.success = success;
this.status = status;
}
public Result(boolean success, String msg, String status, String info) {
this.success = success;
this.msg = msg;
this.status = status;
this.info = info;
}
public static <T> Result<T> success(String msg) {
return new Result(true, msg, STATUS_Y, msg);
}
public static <T> Result<T> success() {
return new Result(true, STATUS_Y);
}
public static <T> Result<T> success(T data) {
return new Result(true, "", data);
}
public static <T> Result<T> error(String error) {
return new Result(false, error, STATUS_N, error);
}
}
- JSON-SCHEMA
JSON Schema
- 定义
JSON 是目前应用非常广泛的数据交换格式。既然是用于数据交换的格式,那么就存在数据交换的双方。如何约定或校验对方的数据格式是符合要求的,就成了服务交互需要解决的一个问题。所以 JSON Schema 就是用来定义 JSON 数据约束的一个标准。根据这个约定模式,交换数据的双方可以理解 JSON 数据的要求和约束,也可以据此对数据进行验证,保证数据交换的正确性。 - 版本
The current version is 2019-09! - 格式
{
" $schema" : "http://json-schema.org/draft-07/schema#",
"$ id" : "http://example.com/product.schema.json",
"description" : "A product from Acme's catalog",
"properties" : {
"dimensions" : {
"properties" : {
"height" : {
"type" : "number"
},
"length" : {
"type" : "number"
},
"width" : {
"type" : "number"
}
},
"required" : [ "length", "width", "height" ],
"type" : "object"
},
"price" : {
"description" : "The price of the product",
"exclusiveMinimum" : 0,
"type" : "number"
},
"productId" : {
"description" : "The unique identifier for a product",
"type" : "integer"
},
"productName" : {
"description" : "Name of the product",
"type" : "string"
},
"tags" : {
"description" : "Tags for the product",
"items" : {
"type" : "string"
},
"minItems" : 1,
"type" : "array",
"uniqueItems" : true
}
},
"required" : [ "productId", "productName", "price" ],
"title" : "Product",
"type" : "object"
}
-
分析说明:
" $schema": "http://json-schema.org/draft-07/schema#"
说明当前使用的schema版本,可以不包含
"$ id": "http://example.com/product.schema.json"
当前 schema 的唯一 id 标识,一般指向一个自主域名。方便后续引用,可以不包含
"title": "Product"
当前 schema 的标题,简要描述信息,可不包含
"description": "A product from Acme's catalog"
详细描述信息,可不包含
"type": "object"
约束对象是 object,也就是在 { } 中的数据
"properties": {
"productId": {
"description": "The unique identifier for a product",
"type": "integer"
}object 中具体属性的约束,description 是描述信息,不产生具体约束。
type 约束 productid 属性类型为整型
"productName": {
"description": "Name of the product",
"type": "string"
},约束 productName 属性类型为字符型
"price": {
"description": "The price of the product",
"type": "number",
"exclusiveMinimum": 0
}约束 price 属性类型为数字型,可以是整型或浮点型。
exclusiveMinimum 约束该数字 >0(不包含 0)
"tags": {
"description": "Tags for the product",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
}约束 tag 属性是 array 数组。items 是数组项约束,这里约束数组项均为字符型
minItems 数组至少包含 1 项。
uniqueItems 约束数组中每项不得重复
{ "dimensions" : { "properties" : { "height" : { "type" : "number" }, "length" : { "type" : "number" }, "width" : { "type" : "number" } }, "required" : [ "length", "width", "height" ], "type" : "object" } }
约束 dimensions 嵌套对象,其中 length,width,height 均为数字类型
且这三个字段在 dimensions 对象中必须包含
{ "required" : [ "productId", "productName", "price" ] }
当前数据对象必须包含 productId,productName,price 三个字段
个人总结
Swagger bootstrap ui
- swagger-ui需要配置的地方这里也要
- swagger-bootstrap-ui官网:https://doc.xiaominfo.com/
- Spring Boot导入POM依赖
<!--swagger-api 依赖开始-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.2</version>
</dependency>
<!--swagger-api 依赖结束-->
总结
项目中采用swagger-ui作为自动生成文档工具非常方便,无需花费额外的时候维护接口文档,而swagger-bootstrap-ui更强大,可以在线生成.md格式接口文档,除了开发人员自己使用外,导出作为项目交付物也非常方便,个人建议Spring Boot项目中使用swagger-bootstrap-ui作为接口文档工具是最佳方案,本人所在项目自从使用了该插件后,再没用过其他类似工具,得到团队一致肯定,测试也经常采用该工具测试接口。
总之一句话,选择swagger-bootstrap-ui就对了,你会爱上它的。