一、小故事
故事情节:后端又更新了API接口文档,发在交流群里。
在日常工作中,API版本每次更新的时候,需要再次发送一份给前端,容易造成文档交流不及时。尤其是现在提倡前后端分离,更容易造成文档和代码的不一致。本篇文章就来分享一种API文档维护的方式,即通过Swagger来自动生成Restful API文档。
二、Swagger简介
Swagger是一款Restful接口的文档在线自动生成和功能测试功能软件。
Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化Restful风格的Web服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。
三、Swagger优缺点
优点
节省了大量手写接口文档的时间
通过注解自动生成在线文档
接口在线调用调试
缺点
代码耦合,需要注解支持
代码侵入性比较强
无法测试错误的请求方式、参数及不限于这些
手动API痛点
文档更新的时候,需要再次发送一份给前端,也就是文档更新交流不及时
接口返回结果不明确
不能直接在线测试接口,通常需要使用工具,比如postman
接口文档太多,不好管理
四、集成配置
引入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.0</version>
</dependency>
基本信息配置
@Configuration
//注解开启 swagger2 功能
@EnableSwagger2
public class Swagger2Config {
//是否开启swagger,正式环境一般是需要关闭的
@Value("${swagger.enabled}")
private boolean enableSwagger;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
//是否开启 (true 开启 false隐藏。生产环境建议隐藏)
.enable(enableSwagger)
.select()
//扫描的路径包,设置basePackage会将包下的所有被@Api标记类的所有方法作为api
.apis(RequestHandlerSelectors.basePackage("com.example.swagger.controller"))
//指定路径处理PathSelectors.any()代表所有的路径
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
//设置文档标题(API名称)
.title("SpringBoot中使用Swagger2构建RESTful接口")
//文档描述
.description("接口说明")
//服务条款URL
.termsOfServiceUrl("http://127.0.0.1:8080/")
//联系信息
.contact("码农新锐")
//版本号
.version("1.0。0")
.build();
}
}
其中:
@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的,作用为:配置spring容器(应用上下文)。用@Bean标注方法等价于XML中配置bean。
@EnableSwagger2的作用是启用Swagger2相关功能。
Docket对象包含三个方面信息:
整个API的描述信息,即ApiInfo对象包括的信息,这部分信息会在页面上展示。
指定生成API文档的包名。
指定生成API的路径。
控制层Controller
代码只贴出了控制层,其他的请看源码。
@RestController
@RequestMapping("user")
@Api(value = "测试接口", tags = "UserController", description = "测试接口相关")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/save", method = RequestMethod.POST)
//@ApiImplicitParam(name = "user", value = "用户实体user", required = true, dataType = "User")
@ApiOperation(value = "创建用户", notes = "创建用户")
public Map<String, Object> saveUser(@ApiParam(required = true, name = "user", value = "用户实体user") @RequestBody @Valid User user) {
Map<String, Object> ret = new HashMap<>();
try {
if (null == user) {
ret.put("msg", "参数不能为空");
return ret;
}
int row = userService.createUser(user);
if (row > 0) {
ret.put("msg", "添加成功");
} else {
ret.put("msg", "添加失败");
}
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
/**
* @param id
* @return
*/
@RequestMapping(value = "/get/{id}", method = RequestMethod.GET, produces = "application/json")
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long", paramType = "query")
@ApiOperation(value = "获取用户详细信息", notes = "根据id来获取用户详细信息")
public Map<String, Object> getUser(@RequestParam Long id) {
Map<String, Object> ret = new HashMap<>();
try {
User user = userService.selectById(id);
if (null == user) {
ret.put("msg", "用户ID:" + id + ",未找到数据");
} else {
ret.put("msg", "获取成功");
ret.put("data", user);
}
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
/**
* paramType:指定参数放在哪个地方
* path:(用于restful接口)-->请求参数的获取:@PathVariable
*
* @param id
* @return
*/
@RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long", paramType = "path")
@ApiOperation(value = "删除用户", notes = "删除用户")
public Map<String, Object> deleteUser(@PathVariable Long id) {
Map<String, Object> ret = new HashMap<>();
if (null == id) {
ret.put("msg", "参数不能为空");
return ret;
}
int row = userService.deleteById(id);
if (row > 0) {
ret.put("msg", "删除成功");
} else {
ret.put("msg", "删除失败");
}
return ret;
}
/**
* ApiIgnore 使用该注解忽略这个API,不会生成接口文档。可注解在类和方法上
* @return
*/
@ApiIgnore
@RequestMapping(value = "/getAll", method = RequestMethod.GET, produces = "application/json")
@ApiOperation(value = "获取用户列表", notes = "获取用户列表")
public Map<String, Object> getAll() {
Map<String, Object> ret = new HashMap<>();
ret.put("msg", "获取成功");
return ret;
}
}
注解说明
常用注解说明:
@Api:用于controller类上,说明该类的作用
tags=“说明该类的作用,可以在UI界面上看到的注解”
value=“该参数没什么意义,在UI界面上也看到,所以不需要配置”
@ApiOperation:用在controller的方法上,用来说明方法用途、作用
value=“说明方法的用途、作用”
notes=“方法的备注说明”
@ApiImplicitParam:用来给方法入参增加说明
name:参数名
value:参数的汉字说明、解释
dataType: 参数类型,默认String
required : 参数是否必传,true必传
defaultValue:参数的默认值
paramType:参数放在哪个地方
header请求参数的获取:@RequestHeader,参数从请求头获取
query请求参数的获取:@RequestParam,参数从地址栏问号后面的参数获取
path(用于restful接口)请求参数的获取:@PathVariable,参数从URL地址上获取
body(不常用)参数从请求体中获取
form(不常用)参数从form表单中获取
@ApiImplicitParams:用在controller的方法上,一组@ApiImplicitParam
@ApiResponse:用在 @ApiResponses里边,一般用于表达一个错误的响应信息
code:数字,例如400
message:信息,例如"请求参数没填好"
response:抛出异常的类
@ApiResponses:用在controller的方法上,用于表示一组响应
@ApiModel:用在返回对象类上,描述一个Model的信息(这种一般用在post创建的时候,使用@RequestBody这样的场景,请求参数无法使用@ApiImplicitParam注解进行描述的时候)
@ApiModelProperty:用在出入参数对象的字段上,表示对model属性的说明或者数据操作更改
value = 字段说明
name = 重写属性名字
dataType = 重写属性类型
required = 是否必填,true必填
example = 举例说明
hidden = 隐藏
@ApiIgnore:使用该注解忽略这个API,不会生成接口文档。可注解在类和方法上
五、Swagger页面访问
启动项目后,输入http://localhost:8080/swagger-ui.html访问,页面如下:
点击需要访问的api列表,即可弹出如下页面,填写相关信息,然后点击Try it out!按钮
其中:
201 表示请求成功并且服务器创建了新的资源
401 表示未授权,请求要求身份验证
403 表示拒绝访问
404 找不到路径,即路径错误
六、遇到的问题
1、没有编写Swagger配置类,会出现这样的错误
2、如果paramType与方法参数获取使用的注解不一致,会直接影响到参数的接收。
如下图,paramType设置header,应该用@RequestHeader接收,但用了@RequestParam接收参数,就会报错。
接收参数不一致报错如下:
3、API列表中同一个方法出现多个
原因:get方法没有在RequestMapping中指定method类型。
解决方案:控制层中定义的方法必须在@RequestMapper中显示的指定RequestMethod类型(get/post等),否则SawggerUi会默认为全类型皆可访问, API列表中会生成多条项目。
七、总结
本章节主要是对Swagger的简介和集成进行了简单介绍,以及对常用注解的说明,想必大家有了初步了解。如有不懂可以在公众号留言。注意在生产环境中关闭Swagger接口文档,避免暴漏的接口被有心人攻击。
源码在这里GitHub:
https://github.com/xiaonongOne/springboot-swagger.git