SpringBoot系列之Springboot集成Swagger

1. Swagger的产生

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

由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。

随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。

Swagger的产生就是为了解决以上这些问题。

2. Swagger的介绍

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单。

接下来我们介绍spring boot怎么集成swagger。

3. 新建maven java project

新建一个maven java project取名为:spring-boot-Swagger

4. 在pom.xml添加依赖

主要是springfox-swagger2和springfox-swagger-ui,版本号根据决定

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

5. 创建Swagger2配置类

编写一个swagger配置类,com.config.SwaggerConfig:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.async.DeferredResult;
import com.google.common.base.Predicates;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
* Swagger配置类:
* @author Angel --守护天使
* @version v.0.1
* @date 2016年9月7日
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {

/**
* SpringBoot默认已经将classpath:/META-INF/resources/和classpath:/META-INF/resources/webjars/映射
* 所以该方法不需要重写,如果在SpringMVC中,可能需要重写定义(我没有尝试)
* 重写该方法需要 extends WebMvcConfigurerAdapter
*
*/
// @Override
// public void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry.addResourceHandler("swagger-ui.html")
// .addResourceLocations("classpath:/META-INF/resources/");
//
// registry.addResourceHandler("/webjars/**")
// .addResourceLocations("classpath:/META-INF/resources/webjars/");
// }
/**
* 可以定义多个组,比如本类中定义把test和demo区分开了
* (访问页面就可以看到效果了)
*
*/
@SuppressWarnings("unchecked")
@Bean
public Docket testApi(){
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("test")
.genericModelSubstitutes(DeferredResult.class)
.useDefaultResponseMessages(false)
.forCodeGeneration(true)
.pathMapping("/")// base,最终调用接口后会和paths拼接在一起
.select()
.paths(Predicates.or(PathSelectors.regex("/api/.*")))//过滤的接口
.build()
.apiInfo(testApiInfo());
return docket;
}
@SuppressWarnings("unchecked")
@Bean
public Docket demoApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("demo")
.genericModelSubstitutes(DeferredResult.class)
// .genericModelSubstitutes(ResponseEntity.class)
.useDefaultResponseMessages(false)
.forCodeGeneration(false)
.pathMapping("/")
.select()
.paths(Predicates.or(PathSelectors.regex("/demo/.*")))//过滤的接口
.build()
.apiInfo(demoApiInfo());
}
private ApiInfo testApiInfo() {
ApiInfo apiInfo = new ApiInfo("Test相关接口",//大标题
"Test相关接口,主要用于测试.",//小标题
"1.0",//版本
"https://griabcrh.github.io/",
"griabcrh",//作者
"griabcrh的博客",//链接显示文字
"https://griabcrh.github.io/"//网站链接
);
return apiInfo;
}
private ApiInfo demoApiInfo() {
ApiInfo apiInfo = new ApiInfo("Demo相关接口",//大标题
"Demo相关接口,获取个数,获取列表,注意:",//小标题
"1.0",//版本
"https://griabcrh.github.io/",
"griabcrh",//作者
"griabcrh的博客",//链接显示文字
"https://griabcrh.github.io/"//网站链接
);
return apiInfo;
}
}

上代码所示,通过@Configuration注解,让Spring来加载该类配置。再通过@EnableSwagger2注解来启用Swagger2。

再通过createRestApi函数创建Docket的Bean之后,apiInfo()用来创建该Api的基本信息(这些基本信息会展现在文档页面中)。select()函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现

经过这2步配置后,我们启动服务后,访问:http://localhost:8080/swagger-ui.html 就完成了集成。

下面编写2个controller进行测试;

6.编写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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package com.controller;

import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.bean.Demo;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.annotations.ApiIgnore;

/**
* DemoController
*
*/
@Controller
@RequestMapping(value = "/demo")
public class DemoController {

private Logger logger = LoggerFactory.getLogger(DemoController.class);
/**
* 可以直接使用@ResponseBody响应JSON
*
* @param request
* @param response
* @return
*/
@ResponseBody
@RequestMapping(value = "/getcount", method = RequestMethod.POST)
@ApiOperation(value="测试-getCount", notes="getCount更多说明")
public ModelMap getCount(HttpServletRequest request,
HttpServletResponse response) {
logger.info(">>>>>>>> begin getCount >>>>>>>>");
ModelMap map = new ModelMap();
map.addAttribute("count", 158);
// 后台获取的国际化信息
map.addAttribute("xstest", "测试");
return map;
}
/**
* 可以直接使用@ResponseBody响应JSON
*
* @param request
* @param response
* @return
*/
@ApiIgnore//使用该注解忽略这个API
@ResponseBody
@RequestMapping(value = "/jsonTest1", method = RequestMethod.POST)
public ModelMap jsonTest(HttpServletRequest request,
HttpServletResponse response) {
ModelMap map = new ModelMap();
map.addAttribute("hello", "你好");
map.addAttribute("veryGood", "很好");
return map;
}
/**
* 可以直接使用@ResponseBody响应JSON
*
* @param request
* @param response
* @return
*/
@ResponseBody
@RequestMapping(value = "/jsonTest3", method = RequestMethod.POST)
public List<String> jsonTest3(HttpServletRequest request,
HttpServletResponse response) {
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("你好");
return list;
}
/**
* JSON请求一个对象<br/>
* (Ajax Post Data:{"name":"名称","content":"内容"})
*
* @param
* @return
*/
@ResponseBody
@RequestMapping(value = "/jsonTest2", method = RequestMethod.POST)
public ModelMap jsonTest2(@RequestBody Demo demo) {
logger.info("demoName:" + demo.getName());
logger.info("demoContent:" + demo.getContent());
ModelMap map = new ModelMap();
map.addAttribute("result", "ok");
return map;
}
/**
* 直接读取URL参数值<br/>
* /demo/jsonTest6.do?name=Hello&content=World
*
* @param demoName
* @param content
* @return
*/
@ResponseBody
@RequestMapping(value = "/jsonTest6", method = RequestMethod.POST)
public ModelMap jsonTest6(@RequestParam("name") String demoName, @RequestParam String content) {
logger.info("demoName:" + demoName);
ModelMap map = new ModelMap();
map.addAttribute("name",demoName + "AAA");
map.addAttribute("content",content + "BBB");
map.addAttribute("date",new java.util.Date());
return map;
}
/**
* JSON请求一个对象,将RequestBody自动转换为JSONObject对象<br/>
* (Ajax Post Data:{"name":"名称","content":"内容"})
*
* 使用JSONObject请添加依赖
* <dependency>
* <groupId>net.sf.json-lib</groupId>
* <artifactId>json-lib</artifactId>
* <version>2.4</version>
* <!--指定jdk版本 -->
* <classifier>jdk15</classifier>
* </dependency>
*
* @param
* @return
*/
@ResponseBody
@RequestMapping(value = "/jsonTest5", method = RequestMethod.POST)
public ModelMap jsonTest5(@RequestBody JSONObject jsonObject) {
String name = jsonObject.getString("name");
logger.info("demoName:" + name);
ModelMap map = new ModelMap();
map.addAttribute("demoName",name);
return map;
}
/**
* 输入和输出为JSON格式的数据的方式 HttpEntity<?> ResponseEntity<?>
*
* @param
* @return
*/
@ResponseBody
@RequestMapping(value = "/jsonTest4", method = RequestMethod.POST)
public ResponseEntity<String> jsonTest4(HttpEntity<Demo> demo,
HttpServletRequest request, HttpSession session) {
//获取Headers方法
HttpHeaders headers = demo.getHeaders();
// 获取内容
String demoContent = demo.getBody().getContent();
// 这里直接new一个对象(HttpHeaders headers = new HttpHeaders();)
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("MyHeaderName", "SHANHY");
ResponseEntity<String> responseResult = new ResponseEntity<String>(
demoContent, responseHeaders, HttpStatus.OK);
return responseResult;
}
}
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
package com.controller;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* Created by Administrator on 2017/8/16.
*/
@Controller
@RequestMapping("/api/test")
public class TestController {

@ResponseBody
@RequestMapping(value = "/show",method = RequestMethod.POST)//这里指定RequestMethod,如果不指定Swagger会把所有RequestMethod都输出,在实际应用中,具体指定请求类型也使接口更为严谨。
@ApiOperation(value="测试接口",notes = "测试接口详细描述")
public String show(
@ApiParam(required = true,name = "name",value = "姓名")
@RequestParam(name="name") String stuName){
return "success";
}
}
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
package com.bean;

/**
* Created by Administrator on 2017/8/16.
*/
public class Demo {

private long id;
private String name;
private String content;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

Swagger2默认将所有的Controller中的RequestMapping方法都会暴露,然而在实际开发中,我们并不一定需要把所有API都提现在文档中查看,这种情况下,使用注解@ApiIgnore来解决,如果应用在Controller范围上,则当前Controller中的所有方法都会被忽略,如果应用在方法上,则对应用的方法忽略暴露API。

在访问http://localhost:8080/swagger-ui.html就可以看到如下效果: