Spring Boot中使用Swagger2构建强大的RESTful API文档
更多干货
- spring-boot系列一 之restfull api与多环境配置
- springboot系列二之 日志
- SpringBoot系列三之 MVC 模版引擎
- SpringBoot 2.0.0.M7 系列四 异常处理
- springboot 2.0.0.M7之 注解 与 配置
- springboot 2.0.0.M7 配置mvc
- springboot 2.0.0.M7 之 Servlet Listener Filter
- springboot 2.0.0.M7 之 跨域
- springboot 2.0.0.M7 之使用mysql
- spring boot 2.0.0.M7 之 数据库-事务处理
- springboot 2.0.0.M7 之 h2 嵌入式数据库的使用
- springboot 2.0.0.M7 之 数据库-redis
- Spring Boot 2.0.0.M7 中使用Swagger2构建RESTful API文档
- Spring Boot 2.0.0.M7 springBoot-mongodb使用
- Spring Boot 2.0.0.M7 使用 Caching-EhCache
- Spring Boot spring boot 使用 Caching-Redis
- Spring Boot 2.0.0.M7 使用异步消息服务-AMQP(RabbitMQ)
- Spring Boot 2.0.0.M7 调用REST服务-如何使用代理
- Spring Boot 2.0.0.M7 发送邮件-使用模板邮件并实现多账号轮询发送
- Spring Boot 2.0.0.M7 使用Spring Session实现集群-redis
- Spring Boot 2.0.0.M7 如何进行远程调试
- Spring Boot 生产准备-基于HTTP的监控
- Spring Boot 集成 Druid
- springboot思维导图
由于Spring Boot能够快速开发、便捷部署等特性,相信有很大一部分Spring Boot的用户会用来构建RESTful API。而我们构建RESTful API的目的通常都是由于多终端的原因,这些终端会共用很多底层业务逻辑,因此我们会抽象出这样一层来同时服务于多个移动端或者Web前端。
问题
这样一来,我们的RESTful API就有可能要面对多个开发人员或多个开发团队:IOS开发、Android开发或是Web开发等。为了减少与其他团队平时开发期间的频繁沟通成本,传统做法我们会创建一份RESTful API文档来记录所有接口细节,然而这样的做法有以下几个问题:
- 由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。
- 随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象
- Swagger2,它可以轻松的整合到Spring Boot中,并与Spring MVC程序配合组织出强大RESTful API文档。它既可以减少我们创建文档的工作量,同时说明内容又整合入实现代码中,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明。另外Swagger2也提供了强大的页面测试功能来调试每个RESTful API。
构建Swagger2
添加Swagger2依赖
<springfox-swagger2.version>2.2.2</springfox-swagger2.version>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox-swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-swagger2.version}</version>
</dependency>
创建Swagger2配置类
在Application.java同级创建Swagger2的配置类Swagger2。
@Configuration
@EnableSwagger2
public class Swagger2 {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.cto.edu"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("架构师的成长之路RESTful APIs")
.description("官网 http://111.231.142.109/")
.termsOfServiceUrl("http://localhost:8081/")
.contact("ctoDev")
.version("1.0")
.build();
}
}
添加文档内容
通过@ApiOperation注解来给API增加说明、通过@ApiImplicitParams、@ApiImplicitParam注解来给参数增加说明
@RestController
@RequestMapping("/api/blog/topic")
public class BlogTopicController {
private static final Logger LOG = LoggerFactory.getLogger(BlogTopicController.class);
@Resource
private BlogTopicFacade blogTopicFacade;
@ApiOperation(value = "分页获取主题列表", notes = "根据类别获取类别下的主题列表")
@ApiImplicitParam(name = "obj", value = "{\"categoryIds\":[],\"number\":0,\"size\":1}", required = true, dataType = "JSONObject")
@SuppressWarnings("unchecked")
@PostMapping(value = "/findForPage")
public ViewerResult findForPage(@RequestBody JSONObject obj) {
ViewerResult result = new ViewerResult();
try {
List<String> categoryIds = (List<String>) obj.get("categoryIds");
int number = obj.getInteger("number");
int size = obj.getInteger("size");
Pageable page = PageRequest.of(number, size);
Searchable searchable = Searchable.newSearchable();
searchable.setPage(page);
if (categoryIds == null || categoryIds.isEmpty()) {
Page<BlogTopic> topicPage = blogTopicFacade.listPage(searchable);
result.setData(topicPage);
} else {
Page<BlogTopic> topicList = blogTopicFacade.findTopicByCategoryIds(categoryIds, page);
result.setData(topicList);
}
result.setSuccess(true);
} catch (Exception e) {
result.setSuccess(false);
result.setErrMessage("查找主题失败");
LOG.error(e.getMessage(), e);
}
return result;
}
@ApiOperation(value = "获取主题信息", notes = "根据Id获取主题信息")
@ApiImplicitParam(name = "obj", value = "{\"id\":\"1\"}", required = true, dataType = "JSONObject")
@PostMapping(value = "/findById")
public ViewerResult findById(@RequestBody JSONObject obj) {
ViewerResult result = new ViewerResult();
BlogTopic topic = null;
try {
String id = obj.getString("id");
topic = blogTopicFacade.getById(id);
result.setSuccess(true);
result.setData(topic);
} catch (Exception e) {
result.setSuccess(false);
result.setErrMessage(e.getMessage());
LOG.error(e.getMessage(), e);
}
return result;
}
}
API详细说明
作用范围 | API | 使用位置 |
---|---|---|
对象属性 | @ApiModelProperty | 用在出入参数对象的字段上 |
协议集描述 | @Api | 用于controller类上 |
协议描述 | @ApiOperation | 用在controller的方法上 |
Response集 | @ApiResponses | 用在controller的方法上 |
Response | @ApiResponse | 用在 @ApiResponses里边 |
非对象参数集 | @ApiImplicitParams | 用在controller的方法上 |
非对象参数描述 | @ApiImplicitParam | 用在@ApiImplicitParams的方法里边 |
描述返回对象的意义 | @ApiModel | 用在返回对象类上 |
@ApiImplicitParam
- paramType 查询参数类型
- dataType 参数的数据类型 只作为标志说明,并没有实际验证
- name 接收参数名
- value 接收参数的意义描述
- required 参数是否必填
- defaultValue 默认值
paramType 示例详解
query 直接跟参数完成自动映射赋值
@GetMapping(value = "/sayParam")
@ApiOperation(value = "信息应答", notes = "返回基本信息")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "info", value = "应答内容", dataType = "String")
})
@RequestParam(value = "info", required = false, defaultValue = "架构师的成长之路") String myInfo
path 以地址的形式提交数据
@RequestMapping(value = "/findById1/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@PathVariable(name = "id") Long id
body 以流的形式提交 仅支持POST
@ApiImplicitParams({ @ApiImplicitParam(paramType = "body", dataType = "MessageParam", name = "param", value = "信息参数", required = true) })
@RequestMapping(value = "/findById3", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@RequestBody MessageParam param
提交的参数是这个对象的一个json,然后会自动解析到对应的字段上去,也可以通过流的形式接收当前的请求数据,但是这个和上面的接收方式仅能使用一个(用@RequestBody之后流就会关闭了)
header 参数在request headers 里边提交
@ApiImplicitParams({ @ApiImplicitParam(paramType = "header", dataType = "Long", name = "id", value = "信息id", required = true) })
String idstr = request.getHeader("id");
if (StringUtils.isNumeric(idstr)) {
id = Long.parseLong(idstr);
}
Form 以form表单的形式提交 仅支持POST
@ApiImplicitParams({ @ApiImplicitParam(paramType = "form", dataType = "Long", name = "id", value = "信息id", required = true) })
@RequestMapping(value = "/findById5", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
访问
启动Spring Boot程序,访问:http://localhost:8081/swagger-ui.html