swagger2和spring boot整合构建RESTful API文档

参考文章:http://blog.didispace.com/springbootswagger2/

这里在这篇文章的基础上加了自己的一些理解和一些没有讲到,但是常用的功能

由于Spring Boot能够快速开发、便捷部署等特性,相信有很大一部分Spring Boot的用户会用来构建RESTful API。而我们构建RESTful API的目的通常都是由于多终端的原因,这些终端会共用很多底层业务逻辑,因此我们会抽象出这样一层来同时服务于多个移动端或者Web前端。

这样一来,我们的RESTful API就有可能要面对多个开发人员或多个开发团队:IOS开发、Android开发或是Web开发等。为了减少与其他团队平时开发期间的频繁沟通成本,传统做法我们会创建一份RESTful API文档来记录所有接口细节,然而这样的做法有以下几个问题:

  • 由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。
  • 随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。

为了解决上面这样的问题,本文将介绍RESTful API的重磅好伙伴Swagger2,它可以轻松的整合到Spring Boot中,并与Spring MVC程序配合组织出强大RESTful API文档。它既可以减少我们创建文档的工作量,同时说明内容又整合入实现代码中,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明。另外Swagger2也提供了强大的页面测试功能来调试每个RESTful API。

添加Swagger2依赖

pom.xml中加入Swagger2的依赖坐标

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>

建立Swagger2配置类

注意这个配置类需要放到Application.java所在的同一目录或者子目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@SpringBootConfiguration
public class Swagger2Config {

@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.itcrud.swagger"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(parameterBuilder());
}

//API的基本信息
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("IT-CRUD接口文档")
.description("专注于学习笔记,想和我一起学习,请关注博客:http://blog.itcrud.com")
.termsOfServiceUrl("http://blog.itcrud.com")
.contact("IT-CRUD")
.version("1.0.0")
.build();
}

//请求参数构建
private List<Parameter> parameterBuilder() {
List<Parameter> parameters = Lists.newArrayList();
parameters.add(new ParameterBuilder().name("token").description("登录验证")
.modelRef(new ModelRef("string")).required(false)
.parameterType("header").build());
return parameters;
}
}

通过createRestApi函数创建Docket的Bean之后,apiInfo()用来创建该Api的基本信息(这些基本信息会展现在文档页面中)。select()函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现,本例采用指定扫描的包路径来定义,Swagger会扫描该包下所有Controller定义的API,并产生文档内容(除了被@ApiIgnore指定的请求)。另外一般项目可能都会用到请求头信息,这里通过ParameterBuilder构建头信息部分,添加到全局选项参数globalOperationParameters中。

添加文档内容

常用注解

在完成了上述配置后,其实已经可以生成文档内容,但是这样的文档主要针对请求本身,而描述主要来源于函数等命名产生,对用户并不友好,我们通常需要自己增加一些说明来丰富文档内容。需要介绍几个常用的注解。

  • @Api:放在Controller类上面,对类做整体的介绍,主要使用descriptionvalue属性
  • @ApiOperation:放在接口方法上,用来描述方法的信息,常用属性是valuenotes
  • @ApiModel:放在接收请求参数类或者响应参数类上,用来描述类信息,常用属性是value
  • @ApiModelProperty:方法接收请求参数类或者响应参数类的属性字段上,用来描述字段信息,常用属性是value
  • @ApiImplicitParam:放在接口方法上,用来描述参数信息,常用属性有namevaluerequireddataTypeparamType(此注解不推荐用)
  • @ApiImplicitParams:放在接口方法上,用来描述参数信息,描述多个字段信息,其内部属性就一个,是@ApiImplicitParam数组,所以常用属性也是namevaluerequireddataTypeparamType(此注解不推荐用)
Controller代码

这是个Controller类,里面包含了上面的所有注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Api(description = "UserController", value = "用户服务接口")
@Controller
@RequestMapping("/user")
public class UserController {

@ApiOperation("添加用户信息")
@PostMapping("/addUser")
@ResponseBody
public String addUser(@RequestBody UserReqDTO reqDTO) {
return "";
}

@ApiOperation("删除用户信息")
@DeleteMapping("/deleteUser")
@ResponseBody
public String deleteUser(@RequestParam(name = "userId") Long userId) {
return "";
}

@ApiOperation("编辑用户信息")
@PutMapping("/editUser")
@ResponseBody
public String editUser(@RequestBody UserReqDTO reqDTO) {
return "";
}

@ApiOperation(value = "查询用户信息", notes = "查询用户信息")
@GetMapping("/getUser")
@ResponseBody
public List<User> getUser(@ModelAttribute UserSearchReqDTO reqDTO) {
return Lists.newArrayList();
}

@ApiOperation(value = "测试header信息", notes = "测试swagger配置中添加的header参数token在传入的时候是否在头信息里面")
@GetMapping("/testHeader")
@ResponseBody
public String testHeader(HttpServletRequest request) {
System.out.println("header-->" + request.getHeader("token"));
return "";
}

@ApiOperation(value = "模糊查询用户", notes = "根据手机号、用户名模糊查询用户信息")
@GetMapping("/getUserByKeywords")
@ApiImplicitParams({
@ApiImplicitParam(name = "userName", value = "用户名", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userPhone", value = "手机号码", required = true, dataType = "String", paramType = "query")
})
@ResponseBody
public List<User> getUsers(@RequestParam("userName") String userName, @RequestParam("userPhone") String userPhone) {
return Lists.newArrayList();
}
}
涉及的实体类

实体类包含请求的参数类和响应的参数类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@Data
@ApiModel(value = "用户详细信息")
public class User {
@ApiModelProperty(value = "用户ID")
private Long id;
@ApiModelProperty(value = "用户姓名")
private String userName;
@ApiModelProperty(value = "用户手机号码")
private String userPhone;
@ApiModelProperty(value = "用户性别")
private Integer gender;
@ApiModelProperty(value = "用户年龄")
private Integer age;
@ApiModelProperty(value = "注册时间")
private Date registerTime;
}
//-----------------------
@Data
@ApiModel(value = "用户参数类")
public class UserReqDTO {
@ApiModelProperty(value = "用户ID")
private Long id;
@ApiModelProperty(value = "用户姓名")
private String userName;
@ApiModelProperty(value = "用户手机号码")
private String userPhone;
@ApiModelProperty(value = "用户性别")
private Integer gender;
@ApiModelProperty(value = "用户年龄")
private Integer age;
}
//-----------------------
@Data
@ApiModel(value = "用户查询类")
public class UserSearchReqDTO {
@ApiModelProperty(value = "用户编号")
private Long id;
@ApiModelProperty(value = "查询关键字(用户名+手机号模糊查询)")
private String keywords;
}

完成上面的代码就可以启动这个springboot项目了。然后直接访问:http://localhost:8080/swagger-ui.html。这个地址根据实际项目不同,但是都是`swagger-ui.html`结尾。

得到的结果如下:

上面的图是整体页面的数据,再展示一下具体接口内部的细节图。

上面细节图中的描述信息、字段名称、说明文字等对应到注解上的属性可以对照看一下,不多解释啦。另外要解释一下上面说@ApiImplicitParams@ApiImplicitParam不推荐用的原因。

不推荐使用@ApiImplicitParams@ApiImplicitParam原因
  • 当我们接收参数是对象的时候,使用@ApiImplicitParam和参数对象类上的@ApiModel系列的注解有兼容性问题,会打乱swagger上的显示,如下两张对比图:

图一


图二


对比图一和图二,图一是不使用@ApiImplicitParam的效果,图二是和@ApiModel混用的效果。很明显的看出来,不使用@ApiImplicitParam更优。这里的请求类UserReqDTO类的参数不多,如果多的话有没有自动填充的效果,会是一件很痛苦的事。

  • 当用@ApiImplicitParam注解来标识参数的时候,需要注明此参数的类型,也就是paramType必填,如果不填很可能导致请求参数不能传入,出现问题。看下面的两张对比图:

图三


图四


这两个图是请求同一个接口(GET请求),图三请求的时候,该接口的@ApiImplicitParam注解将paramType值设置为了query,图四是将这个属性去掉,默认的paramType就是body。很容易就会出错。那么应该怎么设置这个类型呢?有哪些类型?下面看一下:

  1. path:参数是链接上的占位符,如:http://blog.itcrud.com/api/getUserById/{id},id参数对应的类型就是path
  2. query:get请求,拼在地址后面的。如http://blog.itcrud.com/api/getUserById?id=123,id参数对应的类型就是query
  3. body:post请求对应的body体,这个就不多说啦(json体写着麻烦,自己意会)
  4. header:请求头数据,在请求的时候,常用@RequestHeader来接收的参数就是请求头内的数据
  5. form:form表单,不解释

从上面的两个例子,加上对@ApiImplicitParam的巨坑参数paramType的理解,就可以知道为什么这个注解不推荐使用啦。但是在请求参数比较多,而且零散的时候可以考虑使用一下,但是需要注意避坑即可。

Source Code

码云(gitee):https://gitee.com/itcrud/itcrud-note/tree/master/itcrud-note-1-1

微信公众号


本文作者:IT-CRUD
原文地址:http://blog.itcrud.com/blogs/2019/05/swagger-spring-original
版权归作者所有,转载请注明出处

支付宝 微信

如果文章对你有帮助,欢迎点击上方按钮打赏作者