使用工具、框架的目的就是为了开发过程简洁方便或者是达到更强大的功能效果。在目前互联网开发发展的趋势下,由于技术更加深度化、细化,前后端分离开发成为必不可少的一个环节,而后端和前端开发人员之间唯一的桥梁便是API文档,也就是接口文档。
平时开发中大部分接口开发都会由开发者或者特定人员手写接口文档。而文档也会随着项目迭代不停更新,由此需要不停有人员维护API文档的内容。消耗时间同时也需要消耗人力资源。由此原因我有了接触和了解一些API文档生成工具的机会。
了解到了部分的API工具,首先的话是RAP,这个项目好像是阿里开发的,github地址为:https://github.com/thx/RAP 这里的话最后还是选择了swagger,以下先介绍下swagger这个工具
Swagger项目是由Dilip Krishnan和Adrian Kelly等人维护开发的一个为Spring Web MVC 项目提供方法文档的一个框架。该框架最主要的功能是将Controller的方法进行可视化的展现,像方法注释,方法参数,方法返回值等都提供了相应的用户界面,尤其是对JSON参数的支持。同时可以结合swagger-ui可以对用户界面进行不同程度的定制,也可以对方法进行一个简单的测试。
Swagger API框架,用于管理项目中API接口,属当前最流行的API接口管理工具。 Swagger是一个开源框架(Web框架),功能强大,UI界面漂亮,支持在线测试!
Swagger的目标是对REST API定义一个标准的和语言无关的接口,可让人和计算机无需访问源码、文档或网络流量监测就可以发现和理解服务的能力。当通过Swagger进行正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger消除了调用服务时可能会有的猜测。
Swagger是一组开源项目,主要项目如下:
- Swagger-tools:提供各种与Swagger进行集成和交互的工具。例如模式检验、Swagger 1.2文档转换成Swagger 2.0文档等功能。
- Swagger-core: 用于Java/Scala的的Swagger实现。与JAX-RS(Jersey、Resteasy、CXF…)、Servlets和Play框架进行集成。
- Swagger-js: 用于JavaScript的Swagger实现。
- Swagger-node-express: Swagger模块,用于node.js的Express web应用框架。
- Swagger-ui:一个无依赖的HTML、JS和CSS集合,可以为Swagger兼容API动态生成优雅文档。
- Swagger-codegen:一个模板驱动引擎,通过分析用户Swagger资源声明以各种语言生成客户端代码。
- Swagger-editor:可让使用者在浏览器里以YAML格式编辑Swagger API规范并实时预览文档。可以生成有效的Swagger JSON描述,并用于所有Swagger工具(代码生成、文档等等)中。
在尝试使用swagger的过程中碰到了一些问题,看到了一些网友博客都讲的很不错,在下面会列出来以供大家一起学习。首先我们需要构建一个web项目并且引用swagger工具包
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.7.0version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.6.5version>
dependency>
然后我们要建一个swagger的配置类,配置一些当前生成文档的基本信息
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger2 Config
*
* @author hzk
* 正常这里使用@Configuration初始化配置,如果项目引入junit测试需要使用@WebAppConfiguration
*/
@Configuration
@EnableSwagger2
@ComponentScan("com.ithzk.swagger.controller")
public class SpringfoxConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.ithzk.swagger.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
Contact contact = new Contact("HuZeKun", "", "[email protected]");
return new ApiInfoBuilder()
.title("Swagger 1.x API接口文档")
.description("")
.contact(contact)
.version("1.0.0")
.build();
}
}
接下来我们就可以给我们需要生成文档的类加上相应注解了
import com.ithzk.swagger.entity.dto.GoodsDto;
import com.ithzk.swagger.entity.req.GoodsReq;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.validation.Valid;
import java.io.PrintWriter;
/**
* 业务接口
* @author hzk
* @date 2018/4/19
*/
@Controller
@RequestMapping("api/business/")
@Api(value="商业化接口",tags = "接口信息",description = "商业相关接口")
public class BusinessController {
@RequestMapping(value = "goods_info", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "获取商品信息", httpMethod = "GET",response = GoodsDto.class)
public GoodsDto getDeskTopInfo(PrintWriter out, @Valid @ApiParam(value = "params", required = true) GoodsReq req, BindingResult bindingResult) throws Exception {
GoodsDto goodsDto = new GoodsDto();
if (bindingResult.hasErrors()) {
goodsDto.setGoodsId(2);
goodsDto.setGoodsName("上好佳");
} else {
goodsDto.setGoodsId(1);
goodsDto.setGoodsName("浪味仙");
}
return goodsDto;
}
}
import io.swagger.annotations.ApiParam;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
public class BaseInfoRequest {
@ApiParam(name = "version", value = "接口版本号", defaultValue = "1.0", required = true, allowableValues = "1.0")
@Pattern(regexp = "1.0", message = "version filed must equal 1.0")
protected String version;
@ApiParam(name = "format", value = "接口响应格式[json,jsonp]", required = true, defaultValue = "json", allowableValues = "json,jsonp")
@NotNull
protected String format;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
@Override
public String toString() {
return "BaseInfoRequest{" +
"version='" + version + '\'' +
", format='" + format + '\'' +
'}';
}
}
import com.ithzk.entity.req.base.BaseInfoRequest;
import io.swagger.annotations.ApiParam;
import javax.validation.constraints.NotNull;
/**
* 商品请求相关
* @author hzk
* @date 2018/4/19
*/
public class GoodsReq extends BaseInfoRequest {
@ApiParam(name = "goodsType", value = "商品类别", required = true)
@NotNull
private Integer goodsType;
public Integer getGoodsType() {
return goodsType;
}
public void setGoodsType(Integer goodsType) {
this.goodsType = goodsType;
}
@Override
public String toString() {
return "GoodsReq{" +
"goodsType=" + goodsType +
'}';
}
}
import com.alibaba.fastjson.annotation.JSONType;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* 商品相关
* @author hzk
* @date 2018/4/19
*/
@JSONType(orders={"goodsId","goodsName"})
@ApiModel(value = "goods",description = "商品信息")
public class GoodsDto {
@ApiModelProperty(value = "商品ID",name = "goods_id")
private Integer goodsId;
@ApiModelProperty(value = "商品名称",name = "goods_name")
private String goodsName;
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
@Override
public String toString() {
return "GoodsDto{" +
"goodsId=" + goodsId +
", goodsName='" + goodsName + '\'' +
'}';
}
}
上面这些都准备好了之后,我们启动项目,访问项目/v2/api-docs会展现以下格式生成的数据就证明我们的文档注解解析成功
同样我们需要先引入swagger-ui工具包
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.9.2version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.9.2version>
dependency>
启动项目访问http://localhost:8080/swagger-ui.html即可,若出现以下界面则表示搭建成功
也有部分同学会出现以下情况无法加载进入ui界面
这里只需在SwaggerUiApplication上添加@EnableSwagger2注解就能解决问题
package com.swagger.ui;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@EnableSwagger2
public class SwaggerUiApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerUiApplication.class, args);
}
}
这里是一个老版本swagger-ui加载项目生成文档的内容生成的数据
通过点击图上Try it out可以访问并测试接口功能,至此就完成了swagger的嵌入
这里再介绍一下第二种方式搭建swagger-ui的方式,同理需要构建一个spring-web项目并引入对应pom,这种方式可能属于比较旧的swagger1的搭建方式,若大家使用推荐使用上面的swagger2,相对也有很多改进
<dependency>
<groupId>com.mangofactorygroupId>
<artifactId>swagger-springmvcartifactId>
<version>1.0.2version>
dependency>
<dependency>
<groupId>com.mangofactorygroupId>
<artifactId>swagger-modelsartifactId>
<version>1.0.2version>
dependency>
<dependency>
<groupId>com.wordnikgroupId>
<artifactId>swagger-annotationsartifactId>
<version>1.3.11version>
dependency>
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>15.0version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-annotationsartifactId>
<version>2.4.4version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.4.4version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-coreartifactId>
<version>2.4.4version>
dependency>
<dependency>
<groupId>com.fasterxmlgroupId>
<artifactId>classmateartifactId>
<version>1.1.0version>
dependency>
配置swagger基本信息以及spring整合扫描初始化配置
package com.swagger.config;
import com.mangofactory.swagger.configuration.SpringSwaggerConfig;
import com.mangofactory.swagger.models.dto.ApiInfo;
import com.mangofactory.swagger.plugin.EnableSwagger;
import com.mangofactory.swagger.plugin.SwaggerSpringMvcPlugin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableSwagger
@ComponentScan("com.swagger.controller") //启用组件扫描
public class SwaggerConfig {
private SpringSwaggerConfig springSwaggerConfig;
/**
* Required to autowire SpringSwaggerConfig
*/
@Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig)
{
this.springSwaggerConfig = springSwaggerConfig;
}
/**
* Every SwaggerSpringMvcPlugin bean is picked up by the swagger-mvc
* framework - allowing for multiple swagger groups i.e. same code base
* multiple swagger resource listings.
*/
@Bean
public SwaggerSpringMvcPlugin customImplementation()
{
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
.apiInfo(apiInfo())
.includePatterns(".*?");
}
private ApiInfo apiInfo()
{
ApiInfo apiInfo = new ApiInfo(
"Swagger测试接口",
"这是一个Swagger生成API测试",
"My Apps API terms of service",
"[email protected]",
"My Apps API Licence Type",
"My Apps API License URL");
return apiInfo;
}
}
<mvc:resources mapping="/swagger/**" location="/swagger/"/>
<bean class="com.mangofactory.swagger.configuration.SpringSwaggerConfig" />
<bean class="com.swagger.config.SwaggerConfig"/>
其他Controller和Entity同理,构建完成启动即可,但是通过swagger1搭建的文档路径为/api-docs,这个是需要注意的
该版本swagger-ui需要自行下载对应的渲染包放置项目内
swagger-ui
解压下载后的文件,将里面dist文件夹下内容放置项目中,然后修改index.html内容
访问项目下swagger中index.html,发现也可以达到效果
总结下这次初步学习swagger,确实踩了很多坑,有些问题从网上找到的解决方法在自己这里也不管用。总结一下其实如果项目多,单独搭建一个提供查看接口文档的swagger-ui项目,其他项目主要负责生成对应数据格式文件,还是很方便的。有些小伙伴项目嵌入swagger可能会遇到一些小问题,比如生成的文档格式数据、或者原始方式引用swagger-ui被过滤器拦截导致接口生成数据无法读取成功等等,这里特别需要注意的是如果搭建swagger-ui所在的项目需要对/v2/api-docs进行过滤(包括springmvn以及shiro等带有过滤器处理的配置)。总结一下学习技术还是要静下心来,心越乱离成功越远。下面列一些学习过程中看到的不错的博客推荐给大家,分享给大家一起学习,这应该也是博主写博客的其中一个目的吧。
相关博客推荐:
Swagger使用
swagger的使用
swagger编写规范
Swagger框架学习分享
swagger2常用注解说明
使用springfox+swagger2书写API文档
SpringMVC+Swagger UI生成可视图的API文档(详细图解)
Restful形式接口文档生成之Swagger与SpringMVC整合手记
SSM三大框架整合Springfox(Swagger2)步骤以及遇到的一些问题