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屏蔽掉)
- 变动更频繁了。。
- 人更懒了。。