Swagger2在SpringBoot环境下的应用
1. 集成Swagger
1.1 添加依赖
<!--swagger2 start-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<!--引入swagger-ui包-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
1.2 配置类
package com.inn.demo.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfiguration extends WebMvcConfigurerAdapter {
//生产关闭swagger
@Value("${swagger.enable}")
private boolean enableSwagger;
// /**
// * 访问swagger ui 出现404时可以把注释去掉试试
// * 解决资源系统资源目录与swagger ui资源目录冲突问题
// * 这个地方要重新注入一下资源文件,不然不会注入资源的,也没有注入requestHandlerMappping,相当于xml配置的swagger资源配置
// * <mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"/>
// * <mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"/>
// * @param registry
// */
// @Override
// public void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
// registry.addResourceHandler("swagger-ui.html")
// .addResourceLocations("classpath:/META-INF/resources/");
// registry.addResourceHandler("/webjars/**")
// .addResourceLocations("classpath:/META-INF/resources/webjars/");
// super.addResourceHandlers(registry);
// }
// /**
// * 支持分组 groupName
// */
// @Bean(value = "solrRestApi")
// public Docket createSolrRestApi() {
// return new Docket(DocumentationType.SWAGGER_2)
// .apiInfo(apiInfo()).groupName("Solr Demo模块")
// .enable(enableSwagger)
// .select()
// .apis(RequestHandlerSelectors.basePackage("com.inn.demo.modules.solr.web"))
// .paths(PathSelectors.any())
// .build();
// }
@Bean(value = "userRestApi")
public Docket createUserRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
//.groupName("用户管理")
.enable(enableSwagger)
.globalOperationParameters(createCommonParams())//公共参数
.select()
.apis(RequestHandlerSelectors.basePackage("com.inn.demo.modules.user.web"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Demo APIs")
.description("应用实例")
//.termsOfServiceUrl("http://www.demo.com/";)
//.contact(new Contact("开发者1", "", "xxx@163.com"))
.version("1.0")
.build();
}
/**
* 创建公共参数
* @return
*/
private List<Parameter> createCommonParams() {
//添加head参数start
List<Parameter> pars = new ArrayList<Parameter>();
ParameterBuilder tokenPar = new ParameterBuilder();
tokenPar.name("x-access-token").description("令牌").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
pars.add(tokenPar.build());
return pars;
//添加head参数end
}
}
1.3 注解使用
作用范围
|
API
|
使用位置
|
对象属性
|
@ApiModelProperty
|
用在出入参数对象的字段上
|
协议集描述
|
@Api
|
用于controller类上
|
协议描述
|
@ApiOperation
|
用在controller的方法上
|
Response集
|
@ApiResponses
|
用在controller的方法上
|
Response
|
@ApiResponse
|
用在 @ApiResponses里边
|
非对象参数集
|
@ApiImplicitParams
|
用在controller的方法上
|
非对象参数描述
|
@ApiImplicitParam
|
用在@ApiImplicitParams的方法里边
|
描述返回对象的意义
|
@ApiModel
|
用在返回对象类上
|
ApiImplicitParam的相关属性
|
||
属性
|
取值
|
作用
|
paramType
|
path
query
body
header
form
|
参数放在哪个地方:必须要有这个属性
header:header中提交:@RequestHeader获取
query :key=value提交:@RequestParam获取
path :地址中提交:@PathVariable获取
body :json流提交 :@RequestBody获取(限POST)
form :表单提交:@RequestParam获取(限POST)
|
dataType
|
Long
String
|
参数的数据类型 只作为标志说明,并没有实际验证
|
name
|
|
接收参数名
|
value
|
|
接收参数的意义描述
|
required
|
|
参数是否必填
|
|
TRUE
|
必填
|
|
FALSE
|
非必填
|
defaultValue
|
|
默认值
|
ApiImplicitParam 与 ApiParam 的区别
ApiImplicitParam:
- 对Servlets或者非 JAX-RS的环境,只能使用 ApiImplicitParam。
- 在使用上,ApiImplicitParam比ApiParam具有更少的代码侵入性,只要写在方法上就可以了,但是需要提供具体的属性才能配合swagger ui解析使用。
- ApiParam只需要较少的属性,与swagger ui配合更好。
代码实例:
@RestController
@RequestMapping(value = "/user")
@Api(value = "/user", description = "人员基本信息 ")
public class UserController {
static Map<String, User> users = Collections.synchronizedMap(new HashMap<String, User>());
@ApiOperation(value = "获取用户列表", notes = "")
@RequestMapping(value = {"/list"}, method = RequestMethod.GET)
public List<User> getUserList() {
List<User> r = new ArrayList<User>(users.values());
return r;
}
@ApiOperation(value = "创建用户", notes = "根据User对象创建用户")
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
@RequestMapping(value = "add", method = RequestMethod.POST)
public String postUser(@RequestBody User user) {
users.put(user.getId(), user);
return "success";
}
@ApiOperation(value = "获取用户详细信息", notes = "根据url的id来获取用户详细信息")
@ApiParam(name = "id", value = "用户ID", required = true)
@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
public User getUser(@PathVariable(value = "id") String id) {
return users.get(id);
}
@ApiOperation(value = "更新用户详细信息", notes = "根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息")
@RequestMapping(value = "/update/{id}", method = RequestMethod.PUT)
public String putUser(@PathVariable @ApiParam(name = "id", value = "用户ID", required = true) String id,
@RequestBody @ApiParam(name = "user", value = "用户详细实体user", required = true) User user) {
User u = users.get(id);
u.setName(user.getName());
u.setAge(user.getAge());
users.put(id, u);
return "success";
}
@ApiOperation(value = "更新用户名称和年龄", notes = "更新用户名称和年龄")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "String",paramType = "path"),
@ApiImplicitParam(name = "name", value = "用户名", required = true, dataType = "String",paramType = "query"),
@ApiImplicitParam(name = "age", value = "年龄", required = true, dataType = "Integer",paramType = "query"),
@ApiImplicitParam(name = "user", value = "用户信息", required = true, dataType = "User",paramType = "body"),
@ApiImplicitParam(name = "headerName", value = "Header信息", required = true, dataType = "String",paramType = "header")
})
@RequestMapping(value = "/update/info/{id}", method = RequestMethod.POST)
public String updateUserNameAndAge(@PathVariable(value = "id") String id,
@RequestParam(value = "name") String name,
@RequestParam(value = "age") Integer age,
@RequestHeader(value = "headerName") String headerName,
@RequestBody User user) {
User u = users.get(id);
u.setName(name);
u.setAge(age);
users.put(id, u);
return "success";
}
@ApiOperation(value = "删除用户", notes = "根据url的id来指定删除对象")
@ApiParam(name = "id", value = "用户ID", required = true)
@RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
public String deleteUser(@PathVariable String id) {
users.remove(id);
return "success";
}
@ApiOperation(value="删除用户-传递数组", notes="删除对象,传递数组")
@RequestMapping(value="/users/deleteByIds", method = RequestMethod.DELETE)
public void deleteUsers(@ApiParam("用户ID数组") @RequestParam Integer[] ids) {
for (int id:ids){
users.remove(id);
}
}
}
User实体类:
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
@ApiModel(value = "User")
public class User {
@ApiModelProperty(value = "ID")
private String id;
@ApiModelProperty(value = "姓名", required = true)
private String name;
@ApiModelProperty(value = "年龄")
private Integer age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
1.4 访问控制台
按以下步骤配置,项目启动后访问:
1.5 可选配置
在application.properties中加入以下配置,用于设置测试请求的host,默认在swagger ui上做请求测试时都是以/users/1为路径发送请求。
如果需要改变请求的根路径,就需要配置这个参数:
该Host也是swagger-ui发送测试请求的Host, 通常我们会将将接口文档部署在测试服务器,这样就需要设置Host,
否则请求都是通过localhost发送,请求不到测试服务器的接口。
springfox.documentation.swagger.v2.host = yourapp.abc.com
配置获取api docs json数据的请求路径 ,默认为/v2/api-docs:
springfox.documentation.swagger.v2.path = /api
2. 生成静态API文档pdf
2.1 Maven 配置
======属性配置=======
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
<asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
<generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
<asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
<asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
=====依赖配置============
<!--离线文档-->
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>1.1.2.RELEASE</version>
<scope>test</scope>
</dependency>
<!--springfox-staticdocs 生成静态文档-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-staticdocs</artifactId>
<version>2.6.1</version>
</dependency>
<!--swagger2 end-->
============插件配置==========
<!--通过Asciidoctor使得asciidoc生成其他的文档格式,例如:PDF 或者HTML5-->
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
<!--生成PDF-->
<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-pdf</artifactId>
<version>1.5.0-alpha.14</version>
</dependency>
<!-- Comment this section to use the default jruby artifact provided by the plugin -->
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
<!--文档生成配置-->
<configuration>
<sourceDirectory>${asciidoctor.input.directory}</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<attributes>
<doctype>book</doctype>
<toc>left</toc>
<toclevels>3</toclevels>
<numbered></numbered>
<hardbreaks></hardbreaks>
<sectlinks></sectlinks>
<sectanchors></sectanchors>
<generated>${generated.asciidoc.directory}</generated>
</attributes>
</configuration>
<!--因为每次执行只能处理一个后端,所以对于每个想要的输出类型,都是独立分开执行-->
<executions>
<!--html5-->
<execution>
<id>output-html</id>
<phase>test</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html5</backend>
<outputDirectory>${asciidoctor.html.output.directory}</outputDirectory>
</configuration>
</execution>
<!--pdf-->
<execution>
<id>output-pdf</id>
<phase>test</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>pdf</backend>
<outputDirectory>${asciidoctor.pdf.output.directory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
2.2 创建index.adoc文件
路径:项目名/docs/asciidoc/index.adoc
内容:
- include::{generated}/overview.adoc[]
- include::{generated}/definitions.adoc[]
- include::{generated}/paths.adoc[]
2.3 创建生成pdf、html的测试类
package com.inn.demo;
import io.github.robwin.markup.builder.MarkupLanguage;
import io.github.robwin.swagger2markup.GroupBy;
import io.github.robwin.swagger2markup.Swagger2MarkupConverter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import springfox.documentation.staticdocs.SwaggerResultHandler;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@AutoConfigureMockMvc
@AutoConfigureRestDocs(outputDir = "target/generated-snippets")
@RunWith(SpringRunner.class)
@SpringBootTest
public class Swagger2MarkupTest {
private String snippetDir = "target/generated-snippets";
private String outputDir = "target/asciidoc";
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
/**
* 生成api html、pdf
* @throws Exception
*/
@Test
public void Test() throws Exception {
// 得到swagger.json,写入outputDir目录中
mockMvc.perform(get("/v2/api-docs").accept(MediaType.APPLICATION_JSON))
.andDo(SwaggerResultHandler.outputDirectory(outputDir).build())
.andExpect(status().isOk())
.andReturn();
// 读取上一步生成的swagger.json转成asciiDoc,写入到outputDir
// 这个outputDir必须和插件里面<generated></generated>标签配置一致
Swagger2MarkupConverter.from(outputDir + "/swagger.json")
.withPathsGroupedBy(GroupBy.TAGS)// 按tag排序
.withMarkupLanguage(MarkupLanguage.ASCIIDOC)// 格式
.withExamples(snippetDir)
.build()
.intoFolder(outputDir);// 输出
}
}
运行测试类即可生成pdf、html
- 生成的PDF和HTML文件:target/asciidoc/html and target/asciidoc/pdf
3. Swagger-UI 汉化
3.1 添加自定义首页和译文
在resourece目录下创建META-INF
esourece目录,然后创建一个名称为"swagger-ui.html" 的HTML文件
![](https://app.yinxiang.com/shard/s7/res/ad16a638-7fe4-4778-b626-cded77c9460f.png)
html内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="icon" type="image/png" href="webjars/springfox-swagger-ui/images/favicon-32x32.png" sizes="32x32"/>
<link rel="icon" type="image/png" href="webjars/springfox-swagger-ui/images/favicon-16x16.png" sizes="16x16"/>
<link href='webjars/springfox-swagger-ui/css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='webjars/springfox-swagger-ui/css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='webjars/springfox-swagger-ui/css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='webjars/springfox-swagger-ui/css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='webjars/springfox-swagger-ui/css/print.css' media='print' rel='stylesheet' type='text/css'/>
<script src='webjars/springfox-swagger-ui/lib/object-assign-pollyfill.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/handlebars-4.0.5.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/lodash.min.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/backbone-min.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/swagger-ui.min.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/highlight.9.1.0.pack_extended.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/jsoneditor.min.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/marked.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lib/swagger-oauth.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/springfox.js' type='text/javascript'></script> <!--国际化操作:选择中文版 -->
<script src='webjars/springfox-swagger-ui/lang/translator.js' type='text/javascript'></script>
<script src='webjars/springfox-swagger-ui/lang/zh-cn.js' type='text/javascript'></script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="javascript:void(0)">
<img class="logo__img" alt="swagger" height="30" width="30" src="webjars/springfox-swagger-ui/images/logo_small.png" />
<span class="logo__title">在线API</span>
</a>
<form id='api_selector'>
<div class='input'>
<select id="select_baseUrl" name="select_baseUrl"></select>
</div>
<div class='input'>
<input placeholder="http://example.com/api"; id="input_baseUrl" name="baseUrl" type="text"/>
</div>
<div id='auth_container'></div>
<div class='input'><a id="explore" class="header__btn" href="#" data-sw-translate>Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate></div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>
大功告成 我们访问 http://localhost:8080/swagger-ui.html 看看显示效果:
![](https://app.yinxiang.com/shard/s7/res/22975584-c5db-4d8b-a77a-826ac3a6e2bb.png)
3.2 更详细的汉化
如果想进一步调整译文,可以在META-INF
esourceswebjarsspringfox-swagger-uilang 目录下添加zh-cn.js文件.
![](https://app.yinxiang.com/shard/s7/res/9d52ede3-6aff-4648-9072-8400ee1e529b.png)
然后在译文(zh-cn.js )内容,如下
'use strict';
/* jshint quotmark: double */
window.SwaggerTranslator.learn({
"Warning: Deprecated":"警告:已过时",
"Implementation Notes":"实现备注",
"Response Class":"响应类",
"Status":"状态",
"Parameters":"参数",
"Parameter":"参数",
"Value":"值",
"Description":"描述",
"Parameter Type":"参数类型",
"Data Type":"数据类型",
"Response Messages":"响应消息",
"HTTP Status Code":"HTTP状态码",
"Reason":"原因",
"Response Model":"响应模型",
"Request URL":"请求URL",
"Response Body":"响应体",
"Response Code":"响应码",
"Response Headers":"响应头",
"Hide Response":"隐藏响应",
"Headers":"头",
"Try it out!":"试一下!",
"Show/Hide":"显示/隐藏",
"List Operations":"显示操作",
"Expand Operations":"展开操作",
"Raw":"原始",
"can't parse JSON. Raw result":"无法解析JSON. 原始结果",
"Example Value":"示例",
"Click to set as parameter value":"点击设置参数",
"Model Schema":"模型架构",
"Model":"模型",
"apply":"应用",
"Username":"用户名",
"Password":"密码",
"Terms of service":"服务条款",
"Created by":"创建者",
"See more at":"查看更多:",
"Contact the developer":"联系开发者",
"api version":"api版本",
"Response Content Type":"响应Content Type",
"Parameter content type:":"参数类型:",
"fetching resource":"正在获取资源",
"fetching resource list":"正在获取资源列表",
"Explore":"浏览",
"Show Swagger Petstore Example Apis":"显示 Swagger Petstore 示例 Apis",
"Can't read from server. It may not have the appropriate access-control-origin settings.":"无法从服务器读取。可能没有正确设置access-control-origin。",
"Please specify the protocol for":"请指定协议:",
"Can't read swagger JSON from":"无法读取swagger JSON于",
"Finished Loading Resource Information. Rendering Swagger UI":"已加载资源信息。正在渲染Swagger UI",
"Unable to read api":"无法读取api",
"from path":"从路径",
"server returned":"服务器返回"
});
大功告成!