0%

Swagger使用简介

Swagger使用简介

Swagger简介

没有API文档工具之前,大家都是手写API文档的,在什么地方书写的都有,有在confluence上写的,有在对应的项目目录下readme.md上写的。这种方式不是说不好,大家都有一个通病,就是懒得更新文档,隔了一段时间,接口变动了什么没人清楚了。另外还有就是开始写文档的时候特别痛苦,每个字段,一行行注释解释。
其实大多数开发都还是写注释的(为了防止自己看不懂吧。。),如果稍加修改,可以从注释中自动生成文档就好了。Hmm..Swagger可以完成这个功能,尤其是针对Java,C#这样的项目,而且是前后端分离的,更加合适了。
Swagger能做什么呢?可以自动根据Controller自动生成对应的文档,并且提供测试接口。如果你能容忍一定程度的代码侵入(也不是太多。。就是在需要暴露的Model和Controller上加点注解,一个配置文件类),Swagger还是很方便的。

Swagger 和 Spring 项目整合

其实很简单,加个依赖,加个配置类,嗯。。这个应该是最低工作保证了。。

先说依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- swagger2 生成对应的Json文档,这个应该可以说是核心依赖了 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<!-- swagger-ui 为项目提供api展示及测试的界面 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<!-- 集成 swagger 的时候,缺少这个 jar包是不OK的-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.6</version>
</dependency>

说真的,依赖包不是太多springfox-swagger2是生成接口访问JSON和核心,同时依赖jackson。界面展示依赖的是swagger-ui。嗯。。引入的时候注意下Jar包冲突。。尤其是Spring的版本不同exclusion一下。。

依赖包这样基本就已经满足了,剩下就是加一个配置类就好了。如下:

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


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;


@Configuration
@EnableSwagger2
public class ApiConfig {
@Bean(name = "docket")
public Docket api() {
ParameterBuilder ticketPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<>();
ticketPar.name("login-token").description("登录Token")
.modelRef(new ModelRef("string")).parameterType("header")
//header中的ticket参数非必填,传空也可以
.required(false).build();
//根据每个方法名也知道当前方法在设置什么参数
pars.add(ticketPar.build());
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.build().globalOperationParameters(pars);
docket.apiInfo(apiInfo());
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("后台接口")
.description("后台接口")
.version("1.0.0")
.build();
}
}

如果项目使用Spring是XML方式,就在配置文件加上如下几行:

1
2
3
<mvc:default-servlet-handler/>
<mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/" />
<mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/" />

如果是SpringBoot的就更简单了。。直接拷贝配置类,加上依赖包就可以运行了。
Hmm。。简单来说这样就足够了。。。
访问地址是默认地址+/swagger-ui.html,我这里是http://localhost:8080/swagger-ui.html
效果如图所示:

稍微复杂一些

上面的只是提供了基础功能,Hmm…还是没有注释说明什么的,只是有一些基础的序列化参数(Json字段还是可以自动识别的,而且能提供默认值)
如果要加入一些注释,就需要对原有项目加入一些侵入代码了(一些swagger特有注解)

首先是Model实体上面的

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
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel
public class AddKeywordRequestVo extends BaseVo {
/**
* 关键词名称
*/
@ApiModelProperty(value = "关键词名称",example = "汽车")
private String keywordName;
/**
*关键词品类,多个用逗号分隔,0全部,1灯泡,2火花塞,3轮胎,4蓄电池,5油品,6雨刮,7全车件。默认为0
*/
@ApiModelProperty(value = "关键词品类,多个用逗号分隔,0全部,1灯泡,2火花塞,3轮胎,4蓄电池,5油品,6雨刮,7全车件。默认为0",example = "1")
private String keywordCategory;
/**
* 关键词拼音
*/
@ApiModelProperty(value = "关键词拼音",example = "qiche")
private String keywordPinyin;
/**
* 关键词同义词
*/
@ApiModelProperty(value = "关键词同义词",example = "卡车")
private String keywordSynonym;
/**
* 词频
*/
@ApiModelProperty(value = "词频",example = "1")
private Integer wordFrequency;
/**
* 词性,0通用,1专属
*/
@ApiModelProperty(value = "词性,0通用,1专属",example = "1")
private Integer wordType;
/**
* 备注
*/
@ApiModelProperty("备注")
private String remark;
public String getKeywordName() {
return keywordName;
}

public void setKeywordName(String keywordName) {
this.keywordName = keywordName;
}

public String getKeywordCategory() {
return keywordCategory;
}

public void setKeywordCategory(String keywordCategory) {
this.keywordCategory = keywordCategory;
}

public String getKeywordPinyin() {
return keywordPinyin;
}

public void setKeywordPinyin(String keywordPinyin) {
this.keywordPinyin = keywordPinyin;
}

public String getKeywordSynonym() {
return keywordSynonym;
}

public void setKeywordSynonym(String keywordSynonym) {
this.keywordSynonym = keywordSynonym;
}

public Integer getWordFrequency() {
return wordFrequency;
}

public void setWordFrequency(Integer wordFrequency) {
this.wordFrequency = wordFrequency;
}

public Integer getWordType() {
return wordType;
}

public void setWordType(Integer wordType) {
this.wordType = wordType;
}

public String getRemark() {
return remark;
}

public void setRemark(String remark) {
this.remark = remark;
}

public AddKeywordRequestVo() {}

public AddKeywordRequestVo(Integer wordFrequency, Integer wordType, String keywordName, String keywordCategory) {
this.wordFrequency = wordFrequency;
this.wordType = wordType;
this.keywordName = keywordName;
this.keywordCategory = keywordCategory;
}
}

其实也不是太多,一个ApiModelProperty,一个ApiModel就够用了,加上上面的注解有什么效果呢?看图吧。。

model的:

参数示例的:

其实Response也是可以的,但是需要为泛型或者具体的,Object类型的就。。。。参考我这边的。。。
如果是泛型的话,参考下图:
model的:

参数示例的:

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

/**
* 关键词Controller
*/
@RestController
@Api("关键词Controller")
public class KeywordsController {
@Autowired
private AddKeyWordService addKeyWordService;
@Autowired
private DelKeyWordService delKeyWordService;
@Autowired
private BatchDelKeyWordService batchDelKeyWordService;

private static Logger logger = LoggerFactory.getLogger(KeyWordController.class);

/**
* 添加关键词
* @param vo 添加关键词请求参数
* @return 添加关键词结果
*/
@RequestMapping(value = "/add/keyword",method = RequestMethod.POST)
@ResponseBody
@ApiOperation("添加关键词")
public Result addKeyword(@RequestBody AddKeywordRequestVo vo){
LogModel lm = LogModel.newLogModel("addKeyword");
logger.info(lm.addMetaData(vo).toJson());
Result res = new Result();
if (!addKeyWordService.checkParam(vo,res)){
return res;
}
try{
addKeyWordService.addKeyword(vo,res);
}catch(Exception e){
res.setStatus(ReturnStatusEnum.SERVICE_ERROR.getValue());
res.setMessage(ReturnStatusEnum.SERVICE_ERROR.getDesc());
}
logger.info(lm.getMeta("status", res.getStatus()).getMeta("meg", res.getMessage()).toJson());
return res;
}

/**
* 删除关键词
* @return 删除关键词结果
*/
@RequestMapping(value = "/del/keyword",method = RequestMethod.DELETE)
@ResponseBody
@ApiOperation("删除关键词")
public Result delKeyword(@RequestBody DelKeywordRequestVo vo){
LogModel lm = LogModel.newLogModel("delKeyword");
logger.info(lm.addMetaData(vo).toJson());
Result res = new Result();
if (!delKeyWordService.checkParam(vo,res)){
return res;
}
try {
delKeyWordService.delKeyword(vo,res);
} catch (Exception e) {
res.setStatus(ReturnStatusEnum.SERVICE_ERROR.getValue());
res.setMessage(ReturnStatusEnum.SERVICE_ERROR.getDesc());
}
logger.info(lm.getMeta("status", res.getStatus()).getMeta("meg", res.getMessage()).toJson());
return res;
}

/**
* 批量删除关键词接口
* @param vo 批量删除关键词请求
* @return 批量删除结果
*/
@ApiOperation("批量删除关键词接口")
@RequestMapping(value = "/batchdel/keyword",method = RequestMethod.DELETE)
@ResponseBody
public Result delKeywords(@RequestBody DelKeywordsRequestVo vo){
LogModel lm = LogModel.newLogModel("delKeywords");
logger.info(lm.addMetaData(vo).toJson());
Result res = new Result();
if (!batchDelKeyWordService.checkParam(vo,res)){
return res;
}
try {
batchDelKeyWordService.delKeywords(vo,res);
} catch (Exception e) {
res.setStatus(ReturnStatusEnum.SERVICE_ERROR.getValue());
res.setMessage(ReturnStatusEnum.SERVICE_ERROR.getDesc());
}
logger.info(lm.getMeta("status", res.getStatus()).getMeta("meg", res.getMessage()).toJson());
return res;
}
}

效果如图所示:

还有更强大的

你以为这就是全部? nonono swagger还可以直接访问接口,Hmm。。这应该是最方便的吧

直接输入对应的参数,点击Try it out! 如图所示:

是不是有了这个连Postman都不用了=-=~

总结

好处

  • 文档跟随项目接口实时改变,不用担心文档和接口不同步
  • 一大批懒开发(比如我)还是写一些注释的,加入注释同时加个注解,也不算太麻烦。
  • 对接流畅多了,前端调用也方便多了。
  • 自己测试也方便

坏处

  • 有一定代码侵入(加入注解,依赖,配置文件等)
  • 放到线上一定一定要屏蔽掉/swagger相关路径(通过Nginx屏蔽掉)
  • 变动更频繁了。。
  • 人更懒了。。