成为一个不可替代的人!
——程二狗
导读
当你好不容易写完接口,你以为已经完事,老大却突然说,接口文档也要写。oh,my God!我快要疯了,然而还是要戳着头发写完。现在好了,Swagger文档让你加少量的注解,自动帮你生成接口文档,系不系很开心ing。当然Swagger也有缺点,为了文档而写很多的Vo类、param类=。
一、Swagger基础介绍
1.什么是Swagger
描述REST API格式的一组规则(规范),OpenAPI(几十个知名互联网公司定制的API文档规范,Swagger就是其实现之一)的前身。Swagger让测试人员和开发人员之间文档共享。用于前后端分离,接口管理和测试工具集。
2.能做什么
2.1.接口的文档在线自动生成
2.2.功能测试
3.版本
3.1.Swagger1(很少用)
3.2.Swagger2(常用)
3.3.springfox-swagger2(用的最多)
三者关系:Swagger2是Swagger1的升级版。spring公司把swagger集成到自己的项目里,整了一个spring-swagger,后来便演变成springfox。springfox本身只是利用自身的aop的特点,通过plug的方式把swagger集成了进来,它本身对业务api的生成,还是依靠swagger来实现。
4.Swagger主要工具
Swagger编辑器:一个基于浏览器的编辑器,您可以在其中编写OpenAPI规范。
Swagger-core: 用于Java/Scala的的Swagger实现。与JAX-RS(Jersey、Resteasy、CXF...)、Servlets和Play框架进行集成。
Swagger-ui:一个无依赖的HTML、JS和CSS集合,可以为Swagger兼容API动态生成优雅文档。
Swagger-codegen:一个模板驱动引擎,通过分析用户Swagger资源声明以各种语言生成客户端代码。
二、注解说明
注解 | 使用位置 | 取值 | 说明 |
---|---|---|---|
@Api | 用于Controller类上 | 协议集描述 | |
value | url的路径位置 | ||
tags | 设置该值,value值被覆盖 | ||
description | 对api资源的描述(过时) | ||
basePath | 基本路径可以不配置(过时) | ||
position | 如果配置了多个Api,可用该注解改变显示顺序(过时) | ||
produces | For example, "application/json, application/xml" | ||
consumes | For example, "application/json, application/xml" | ||
protocols | Possible values: http, https, ws, wss | ||
authorizations | 高级特性认证时配置 | ||
hidden | 配置为true 将在文档中隐藏 | ||
@ApiModel | 用在返回对象类上 | 对返回对象的描述 | |
value | 为模型提供一个替代名称,默认使用类名 | ||
description | 提供一个更长的类描述 | ||
parent | 为模型提供一个超类以允许描述继承 | ||
discriminator | 支持模型继承和多态性,这是用作鉴别器的字段的名称,基于这个字段,可以断言需要使用哪个子类型 | ||
subTypes | 继承自此模型的子类型的数组 | ||
reference | 指定对相应类型定义的引用,覆盖指定的任何其他元数据 | ||
@ApiImplicitParam | 用在controller的方法上 | 对API操作中的单个参数进行定义 | |
name | 参数的名称 | ||
value | 参数的简要描述 | ||
defaultValue | 描述参数的默认值 | ||
allowableValues | 限制此参数的可接受值 | ||
required | 指定是否需要参数,默认false | ||
access | 允许从API文档中过滤参数 | ||
allowMultiple | 指定参数是否可以接受多个逗号分隔的值 | ||
dataType | 参数的数据类型,可以是类名或基元 | ||
dataTypeClass | url的路径位置 | ||
paramType | 参数的参数类型,有效值是路径、查询、正文、标题或表单 | ||
example | 非主体类型参数的一个示例 | ||
examples | 参数示例。仅适用于body参数 | ||
type | 添加覆盖检测到的类型的能力 | ||
format | 添加提供自定义格式的功能 | ||
format | 添加提供自定义格式的功能 | ||
allowEmptyValue | 添加将格式设置为空的功能 | ||
collectionFormat | 添加使用' array '类型覆盖collectionFormat的功能 | ||
@ApiImplicitParams | 用在@ApiImplicitParams的方法里边 | 允许多个ApiImplicitParam对象列表的包装器,参看@ApiImplicitParam注解 | |
@ApiModelProperty | 用在出入参数对象的字段上 | 描述对象属性 | |
value | 属性的简要描述 | ||
name | 允许重写属性的名称 | ||
access | 允许从API文档中过滤参数 | ||
allowableValues | 限制此属性的可接受值 | ||
notes | 目前未使用 | ||
dataType | 参数的数据类型,这可以是类名或基元。该值将覆盖从类属性读取的数据类型 | ||
required | 指定是否需要参数,默认false | ||
position | 允许显式地对模型中的属性进行排序 | ||
hidden | 允许模型属性隐藏在Swagger模型定义中 | ||
example | 属性的示例值 | ||
readOnly | 允许模型属性被指定为只读 | ||
reference | 指定对相应类型定义的引用,覆盖指定的任何其他元数据 | ||
allowEmptyValue | 允许传递空值 | ||
@ApiOperation | 用在controller的方法上 | 用来描述方法的作用 | |
value | 操作的简要描述 | ||
notes | 操作的详细描述 | ||
tags | 标记可用于根据资源或任何其他限定符对操作进行逻辑分组,会覆盖value值 | ||
response | 操作的响应类型 | ||
responseContainer | 声明一个容器,有效值是“List”、“Set”或“Map”。其他任何值都将被忽略 | ||
responseReference | 指定对响应类型的引用,覆盖任何指定的response()类 | ||
httpMethod | 指定一种请求方式 | ||
position | (过时) | ||
nickname | 第三方工具使用operationId来惟一地标识该操作 | ||
produces | 对应于操作的“生成”字段,接受内容类型的逗号分隔值。例如,“application/json, application/xml”将建议此操作生成json和xml输出。 | ||
consumes | 接受内容类型的逗号分隔值。例如,“application/json, application/xml”将建议此API资源接受json和xml输入 | ||
protocols | 为该操作设置特定的协议(方案),可用协议的逗号分隔值。可能的值:http、https、ws、wss。 | ||
authorizations | 获取此操作的授权(安全需求)列表 | ||
hidden | 从操作列表中隐藏操作 | ||
responseHeaders | 响应可能提供的响应头列表 | ||
code | 响应状态码,默认200 | ||
extensions | 可选的扩展数组 | ||
@ApiParam | 用在Controller类的方法上 | ||
name | 参数名称 | ||
value | 参数简要描述 | ||
defaultValue | 参数的默认值 | ||
allowableValues | 限制可接受参数 | ||
required | 指定是否需要参数,默认false | ||
access | 允许从API文档中过滤参数 | ||
allowMultiple | 指定参数是否可以通过多次出现来接受多个值 | ||
hidden | 从参数列表中隐藏参数 | ||
example | 非主体类型参数的一个示例 | ||
examples | 参数示例。仅适用于body参数 | ||
type | 添加覆盖检测到的类型的能力 | ||
format | 添加提供自定义格式的功能 | ||
allowEmptyValue | 添加将格式设置为empty的功能 | ||
readOnly | 增加被指定为只读的能力 | ||
collectionFormat | 添加使用' array '类型覆盖collectionFormat的功能 | ||
@ApiResponse | 用在 @ApiResponses里边 | 返回单个结果集(类)说明 | |
code | 状态码,默认200 | ||
message | 返回响应信息 | ||
response | 可选的响应类来描述消息的有效负载 | ||
reference | 指定对响应类型的引用,覆盖任何指定的response()类 | ||
responseHeaders | 响应可能提供的响应头列表 | ||
responseContainer | 声明一个响应容器,有效值是“List”、“Set”或“Map”。其他任何值都将被忽略 | ||
@ApiResponses | 用在controller的方法上 | 返回结果集说明,参考@ApiResponse | |
@Authorization | 用在controller的方法上 | 定义用于资源或操作的授权方案 | |
value | 用于此资源/操作的授权方案的名称 | ||
scopes | 如果授权模式是OAuth2,则使用的作用域 | ||
@AuthorizationScope | 用在controller的方法上 | 用于定义为已定义授权方案的操作所使用的授权范围 | |
scope | 使用OAuth2授权方案的范围,作用域应该在Swagger对象的securityDefinition部分中预先声明 | ||
description | 用于遗留支持 | ||
@ResponseHeader | 用在controller的方法上 | 响应可能提供的响应头列表 | |
name | 响应头的名字 | ||
description | 响应头的长描述 | ||
response | 响应头的数据类型 | ||
responseContainer | 声明一个响应容器,有效值是“List”、“Set”或“Map”。其他任何值都将被忽略 |
参考:1.Swagger在gitHub上的wiki
2.官网注解文档
allowableValues
限制此属性的可接受值,有三种方法来描述允许的值:
若要设置值列表,请提供一个用逗号分隔的列表,并用方括号括起来。例如:[第一,第二,第三]。
要设置一个值范围,以“range”开头,用方括号括起最小值和最大值。例如:range[1,5]。
要设置最小/最大值,对range使用相同的格式,但使用“无穷大”或“-无穷大”作为第二个值。例如,range[1, infinity]表示该参数的最小允许值为1。
三、与springBoot的集成
如果你还不会快速构建一个springBoot项目,参看我的另一篇文章:springBoot入门简单Demo
3.1.引入jar包
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
3.2.创建Swagger配置类
package com.ergou.springswagger.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger的配置类
* @create by 程二狗 on 2018/11/4 0004
**/
@Configuration//配置类
@EnableSwagger2//启用Swagger
public class SwaggerConfig {
@Bean//加入到spring的容器中
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.pathMapping("/")
.select()
.apis(RequestHandlerSelectors.basePackage("com.ergou.springswagger.controller"))//需要扫描的包路径
.paths(PathSelectors.any())
.build()
.apiInfo(testApiInfo());
}
private ApiInfo testApiInfo() {
return new ApiInfoBuilder()
.title("springBoot集成swagger构建api文档")//标题
.description("详细描述")//详细描述
.version("1.0")//版本
.termsOfServiceUrl("服务地址")
.contact(new Contact("程二狗","https://www.jianshu.com/u/c612609d99d8","chengbotao152@163.com"))//作者的一些信息
.license("The Apache License, Version 2.0")//发布遵循协议
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")//协议地址
.build();
}
}
3.3.测试
在浏览器中输入:http://localhost:8080/swagger-ui.html,端口号改成自己配置的。
成功页面
主要测试类
package com.ergou.springswagger.controller.student;
import com.ergou.springswagger.controller.student.param.*;
import com.ergou.springswagger.controller.student.vo.PageInfoStudent;
import com.ergou.springswagger.controller.student.vo.StudentMapVo;
import com.ergou.springswagger.controller.student.vo.StudentVo;
import com.ergou.springswagger.entity.ApiRes;
import com.ergou.springswagger.entity.PageInfo;
import com.ergou.springswagger.entity.Student;
import com.ergou.springswagger.service.StudentService;
import io.swagger.annotations.*;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* @create by 程二狗 on 2018/10/28 0028
**/
@RestController
@Api(tags = {"学生类操作"})//默认是student-controller
@RequestMapping("/student")
public class StudentController {
@Autowired
StudentService studentService;
//如果我不指定httpMethod,会生成很多请求方式的文档,例如get、post、put==
//当然我如果@PostMapping或者这样@RequestMapping(value = "/add",method = RequestMethod.GET)指定
//就无需再指定如果我不指定httpMethod
@ApiOperation(value = "添加学生",httpMethod = "POST")
@RequestMapping("/add")
@ApiResponses({@ApiResponse(code = 200,message = "success")})
public ApiRes add(@RequestBody StudnetParam param){
return com.ergou.springswagger.entity.ApiResponse.success();
}
@ApiOperation("删除学生")
//我这儿没有指定请求方式,注意看生成的文档
@RequestMapping("/delete")
@ApiResponses({@ApiResponse(code = 200,response = Student.class,message = "success")})
public ApiRes delete(@RequestBody StudnetIdParam param){
return com.ergou.springswagger.entity.ApiResponse.success();
}
@ApiOperation("更新学生")
@PostMapping("/update")
@ApiResponses({@ApiResponse(code = 200,response = Student.class,message = "success")})
public ApiRes update(@RequestBody StudnetParam param){
return com.ergou.springswagger.entity.ApiResponse.success();
}
@ApiOperation("查看学生详情")
@PostMapping("/get")
@ApiResponses({@ApiResponse(code = 200,response = Student.class,message = "success")})
public ApiRes get(@RequestBody StudnetIdParam param){
return com.ergou.springswagger.entity.ApiResponse.success(studentService.get((long)1));
}
//==================重点:以下主要测试@ApiResponses注解==================
//返回List指定
//指定responseContainer,response指定类型
@ApiOperation("获取学生列表")
@PostMapping("/getList")
@ApiResponses({@ApiResponse(code = 200,responseContainer = "List",response = Student.class,message = "success")})
public ApiRes getList(@RequestBody StudnetGetListParam param){
return com.ergou.springswagger.entity.ApiResponse.success(studentService.getList());
}
//返回Map指定测试
//我的做法:构建一个vo类,去包含这些属性
@ApiOperation("返回map结果集测试")
@PostMapping("/getMap")
@ApiResponses({@ApiResponse(code = 200,response = StudentMapVo.class,message = "success")})
public ApiRes getMap(@RequestBody StudnetGetListParam param){
//构建数据
List<StudentVo> studentListVo = new ArrayList<>(4);
studentListVo.add(new StudentVo((long)1,"程二狗", 18, (byte) 1, new Date()));
studentListVo.add(new StudentVo((long)2,"陈雪峰", 18, (byte) 1, new Date()));
studentListVo.add(new StudentVo((long)3,"张金洲", 18, (byte) 1, new Date()));
studentListVo.add(new StudentVo((long)4,"赵楠", 18, (byte) 1, new Date()));
Map<String,Object> studentMap = new HashMap(4){{
put("countInteger",1);
put("studentVo",new StudentVo((long)1,"程二狗", 18, (byte) 1, new Date()));
put("countByte",(byte) 2);
put("studentVoList",studentListVo);
}};
return com.ergou.springswagger.entity.ApiResponse.success(studentMap);
}
//返回PageInfo<T>指定
//我的做法:构建一个vo类,去继承该类,然后指定response
@ApiOperation("返回PageInfo<T>测试")
@PostMapping("/getPageInfo")
@ApiResponses({@ApiResponse(code = 200,response = PageInfoStudent.class, message = "success")})
public ApiRes getPageInfo(@RequestBody StudnetGetListParam param){
PageInfo<StudentVo> pageInfo = new PageInfo<>();
pageInfo.setTotal(2000000);
return com.ergou.springswagger.entity.ApiResponse.success(pageInfo);
}
}
一些注解解释
@Api
@ApiModel
@ApiOperation
PS:其余的注解网上已经很多了,我就不再写了,你一百度就能找到
四、本文重点: @ApiResponses:返回结果集List、Set(同List)、Map、PageInfo如何处理???
//==================重点:以下主要测试@ApiResponses注解==================
//返回List指定
//指定responseContainer,response指定类型
@ApiOperation("获取学生列表")
@PostMapping("/getList")
@ApiResponses({@ApiResponse(code = 200,responseContainer = "List",response = Student.class,message = "success")})
public ApiRes getList(@RequestBody StudnetGetListParam param){
return com.ergou.springswagger.entity.ApiResponse.success(studentService.getList());
}
//返回Map指定测试
//我的做法:构建一个vo类,去包含这些属性
@ApiOperation("返回map结果集测试")
@PostMapping("/getMap")
@ApiResponses({@ApiResponse(code = 200,response = StudentMapVo.class,message = "success")})
public ApiRes getMap(@RequestBody StudnetGetListParam param){
//构建数据
List<StudentVo> studentListVo = new ArrayList<>(4);
studentListVo.add(new StudentVo((long)1,"程二狗", 18, (byte) 1, new Date()));
studentListVo.add(new StudentVo((long)2,"陈雪峰", 18, (byte) 1, new Date()));
studentListVo.add(new StudentVo((long)3,"张金洲", 18, (byte) 1, new Date()));
studentListVo.add(new StudentVo((long)4,"赵楠", 18, (byte) 1, new Date()));
Map<String,Object> studentMap = new HashMap(4){{
put("countInteger",1);
put("studentVo",new StudentVo((long)1,"程二狗", 18, (byte) 1, new Date()));
put("countByte",(byte) 2);
put("studentVoList",studentListVo);
}};
return com.ergou.springswagger.entity.ApiResponse.success(studentMap);
}
//返回PageInfo<T>指定
//我的做法:构建一个vo类,去继承该类,然后指定response
@ApiOperation("返回PageInfo<T>测试")
@PostMapping("/getPageInfo")
@ApiResponses({@ApiResponse(code = 200,response = PageInfoStudent.class, message = "success")})
public ApiRes getPageInfo(@RequestBody StudnetGetListParam param){
PageInfo<StudentVo> pageInfo = new PageInfo<>();
pageInfo.setTotal(2000000);
return com.ergou.springswagger.entity.ApiResponse.success(pageInfo);
}
List(Set)
Map
package com.ergou.springswagger.controller.student.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.List;
/**
* 返回Map结果集测试
* @create by 程二狗 on 2018/11/4 0004
**/
@ApiModel("map")//默认是StudentMapVo,注意看我指定后在界面上是啥样
public class StudentMapVo implements Serializable {
//注意:属性值必须和你map中的key一致
@ApiModelProperty("map测试Integer属性")
private Integer countInteger;
@ApiModelProperty("map测试单对象属性")
private StudentVo studentVo;
@ApiModelProperty("map测试Byte属性")
private Byte countByte;
@ApiModelProperty("map测试List属性")
private List<StudentVo> studentListVo;
public Integer getCountInteger() {
return countInteger;
}
public void setCountInteger(Integer countInteger) {
this.countInteger = countInteger;
}
public StudentVo getStudentVo() {
return studentVo;
}
public void setStudentVo(StudentVo studentVo) {
this.studentVo = studentVo;
}
public Byte getCountByte() {
return countByte;
}
public void setCountByte(Byte countByte) {
this.countByte = countByte;
}
public List<StudentVo> getStudentListVo() {
return studentListVo;
}
public void setStudentListVo(List<StudentVo> studentListVo) {
this.studentListVo = studentListVo;
}
}
PageInfo<T>
package com.ergou.springswagger.controller.student.vo;
import com.ergou.springswagger.entity.PageInfo;
import com.ergou.springswagger.entity.Student;
/**
* @create by 程二狗 on 2018/11/4 0004
**/
public class PageInfoStudent extends PageInfo<StudentVo> {
}
另外我还写了一个User类,和Student类差不多,供大家自行去测试,本文代码百度网盘
链接:https://pan.baidu.com/s/11qmZcE9BBH_cIYa2K5lvQQ
提取码:xkfl
文中若有错误欢迎指正,感谢!后续过程中我也会不断完善
《刹那芳华曲》
朝露昙花
咫尺天涯,人道是黄河十曲,毕竟东流去。
八千年玉老,一夜枯荣,问苍天此生何必?
昨夜风吹处,落英听谁细数。
九万里苍穹,御风弄影,谁人与共?
千秋北斗,瑶宫寒苦,不若神仙眷侣,百年江湖。
程二狗摘自树下野狐《搜神记》
网友评论