作者 | 李增光
杏仁后端工程师。「 只有变秃,才能变强!」
Swagger 是一个流行的API开发框架,这个框架以“开放API声明” (OpenAPI Specification,OAS) 为基础,
对整个 API 的开发周期都提供了相应的解决方案,是一个非常庞大的项目(包括设计、编码和测试,几乎支持所有语言)。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
总体目标是使客户端和文件系统作为服务器以同样的速度来更新。
文件的方法,参数和模型紧密集成到服务器端的代码,允许 API 来始终保持同步。 Swagger 让部署管理和使用功能强大的 API 从未如此简单。
由于 Java 的强大的注解功能,我们使用 SpringBoot 来结合 Swagger2 ,在使用起来非常简单.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
上面两个依赖的作用:
springfox-swagger2 依然是依赖 OSA 规范文档,也就是一个描述 API 的 json 文件,而这个组件的功能就是帮助我们自动生成这个 json 文件
springfox的大致原理就是,在项目启动的过 程 中,Spring 上下文在初始化的过程,
框架自动根据配置加载一些 Swagger 相关的 bean 到当前的上下文中,并自动扫描系统中可能需要生成 API 文档那些类,
并生成相应的信息缓存起来。如果项目MVC控制层用的是 SpringMvc 那么会自动扫描所有 Controller 类,并生成对应的文档描述数据.
该数据是json格式,通过路径:项目地址 /v2/api-docs可以访问到该数据,然后 SwaggerUI根据这份数据生成相应的文档描述界面。
因为我们能拿到这份数据,所以我们也可以生成自己的页面。
springfox-swagger-ui 就是将这个 json 文件解析出来,用一种更友好的方式呈现出来。
由于 Spring 的流行,Marty Pitt 编写了一个基于 Spring 的组件 swagger-springmvc,用于将 Swagger 集成到 SpringMVC 中来。
@RestController
public class UserController {
@RequestMapping("/hello",method = RequestMethod.GET)
public String hello(){
return "hello";
}
}
现在 Swagger2 还不能为我们生成 API 文档,因为我们还没有对它进行配置。
我们需要创建一个配置类,进行如下配置:
@Configuration
@EnableSwagger2
public class SwaggerConfig{
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.itguang.springbootswaggerdemo1.web"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Spring Boot中使用Swagger2构建RESTful API")
.description("rest api 文档构建利器")
.termsOfServiceUrl("http://blog.csdn.net/itguangit")
.contact("itguang")
.version("1.0")
.build();
}
}
springfox 为我们提供了一个 Docket(摘要的意思)类,我们需要把它做成一个 Bean 注入到 spring 中。显然,我们需要一个配置文件,并通过一种方式(显然它会是一个注解)告诉程序,这是一个 Swagger 配置文件。
springfox 允许我们将信息组合成一个 ApiInfo 的类,作为构造参数传给 Docket(当然也可以不构造这个类,而直接使用 Null,但是你的这个 API 就太 low 了)。
现在我们要做的配置已经能满足一个生成 API 文档的基本要求了,让我们启动项目,访问 http://localhost/swagger-ui.html
会看到如下界面:
这是 Swagger-ui 为我们生成的界面.
接下来我们就要好好研究下 springfox-swagger2 给我们提供的注解了.
我们新建一个 Controller,用来对 User 类进行增删改查常用操作。
@RestController
@RequestMapping(value = "/user", produces = APPLICATION_JSON_VALUE) //配置返回值 application/json
@Api(description = "用户管理")
public class HelloController {
ArrayList<User> users = new ArrayList<>();
@ApiOperation(value = "获取用户列表", notes = "获取所有用户信息")
@RequestMapping(value = {""}, method = RequestMethod.GET)
public List<User> hello() {
users.add(new User("逻辑", "luoji"));
users.add(new User("叶文杰", "yewenjie"));
return users;
}
}
可以看到我们在 Controller 上使用了 @Api(description = "用户管理")
注解,在方法上使用了 @ApiOperation(value = "获取用户列表", notes = "获取所有用户信息")
注解。
这会产生什么样的效果呢? 我们可以再次访问下试试看:
可以看到我们红框裱起来的地方发生了改变,并且神奇的是它还自动判断出了我们的返回类型:
[
{
"age": 0,
"email": "string",
"enabled": true,
"id": "string",
"password": "string",
"username": "string"
}
]
但是我们如果想选择性的忽略某个字段,而不是把 User 类中的所有字段暴露出去呢?别着急,我们可以使用另一个注解: @ApiModelProperty(hidden = true)。此注解可以作用在字段或者方法上,只要 hidden 属性为 true ,该字段或者方法就不会被生成 API 文档。
@Data
public class User {
private String id;
private String username;
@ApiModelProperty(hidden = true)
private String password;
private String email;
private Integer age;
private Boolean enabled;
}
我们有意忽略了 password 字段,再次刷新浏览器,会看到:
确实 password 字段不见了。
接下来我们在模拟一个创建用户的 API:
@ApiOperation(value = "创建用户", notes = "根据User对象创建用户")
@RequestMapping(value = "/create", method = RequestMethod.POST)
public User postUser(User user) {
return user;
}
可以看到我们需要客户端传给我们一个 User 对象,用来创建和该用户,这里我们什么也不做,只是把接受到的 User 对象返回给客户端,来表示创建成功。
我们刷新浏览器看下:
可以看到请求参数并不是让我们很满意啊,第一没有字段说明,第一有些字段在创建用户时我们并不需要啊,还是不要着急,我们也有办法解决:
@ApiModelProperty(hidden = true)
private String id;
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(hidden = true)
private Integer age;
@ApiModelProperty(hidden = true)
private Boolean enabled;
我们在User对象的字段上添加 上面的注解: @ApiModelProperty(hidden = true)
和 @ApiModelProperty(value = "用户名")
。
value 属性指明了该字段的含义(描述 Description),再次刷新浏览器试试:
怎么样,是不是很简单。
下面我们看看如何传递参数。添加一个方法,根据 id 获取用户信息:
@ApiOperation(value = "获取用户详细信息", notes = "根据url的id来获取用户详细信息")
@RequestMapping(value = "getUser/{id}", method = RequestMethod.GET)
public User getUser(@PathVariable(value = "id") String id) {
return new User(id, "itguang", "123456");
}
刷新浏览器,可以看到:
我们需要客户端传入一个参数 id,现在我们要给 id 这个参数一个说明(Description),该咋办呢? 还是不要着急,很简单像下面这样即可:
@ApiOperation(value = "获取用户详细信息", notes = "根据url的id来获取用户详细信息")
@RequestMapping(value = "getUser/{id}", method = RequestMethod.GET)
public User getUser(@ApiParam(value = "用户id", required = true) //[注意] @ApiParam与 Controller中方法并列使用,也可以省略的
@PathVariable(value = "id") String id) {
return new User(id, "itguang", "123456");
}
我们添加了 @ApiParam(value = "用户id", required = true)
这个注解,需要注意的是,这个注解方法的参数前面,不能直接用在方法上面。
再次刷新浏览器:
常用注解说明:
通过上面的了解,我们大概已经会使用 Swagger2 了,但我们只介绍了一些简单常用的注解。 下面我们系统的总结一下:
@Api: 描述类/接口的主要用途
可以在上面 创建用户的方法上添加
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
试试看.
@ApiParam: 请求属性
注意与
@ApiModelProperty(hidden = true)
不同,
@ApiIgnore
不能用在模型数据上
@ApiResponse: 响应配置
如
@ApiResponse(code = 400, message = "无效的用户信息")
,注意这只是在 生成的 Swagger 文档上有效,不要和实际的客户端调用搞混了.
通常我们都是统一JSON返回,用不到这个注解
@ApiResponses: 响应集配置
例如: @ResponseHeader(name="head1",description="response head conf")
@ApiModel: 标识请求/相应模型
@ApiModelProperty:添加和操作模型属性的数据
经过上面的介绍,你应该已经会使用 Swagger2 了。 但是对于有些人来说,看上面的英文表示很难受,有没有中文的?
有!
根据官方文档上的提示,在 SpringBoot 下更换界面和语言还是很简单的,首先我们需要对 SpringBoot 的资源目录有个了解:
Spring Boot 默认“约定”从资源目录的这些子目录读取静态资源:
src/main/resources/META-INF/resources
src/main/resources/static (推荐)
src/main/resources/public
举个栗子: 现在static目录下有一张图片,kumamon.jpg,访问地址是 http://localhost:8080/img/kumamon.jpg
注: 若不同静态目录含有相同路径图片,则按上述优先级,即 META-INF/resources 目录优先级最高。
了解了 SpringBoot 的资源目录的优先级,我们来看看之前引入的 springfox-swagger-ui 这个包,打开 Maven 依赖找到它:
展开如下所示:
还记得我们之前为什么浏览器输入 http://localhost/swagger-ui.html 就会看到一个 Swagger 的页面吗,没错就是这里啦。其中 swagger-ui.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>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io"><img class="logo__img" alt="swagger" height="30" width="30" src="webjars/springfox-swagger-ui/images/logo_small.png" /><span class="logo__title">swagger</span></a>
<form id='api_selector'>
<div class='input'>
<select id="select_baseUrl" name="select_baseUrl"/>
</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>
看不懂? 好吧,看不懂也没关系,我们想要汉化该咋办呢?
我们也不能直接在这里修改源码啊,还记得我们前面提到的 Spring boot 资源目录的优先级吗? 没错,我们只需要在记得项目下创建 META-INF 这个资源目录就行啊。
SpringBoot 默认会把我们项目的 src/main/resources/META-INF/resources
覆盖其它的依赖下的文件。
创建 资源目录 src/main/resources/META-INF/resources
,如下:
swagger-ui.html 的内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>itguang</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="http://swagger.io">
<img class="logo__img" alt="swagger" height="30" width="30" src="webjars/springfox-swagger-ui/images/logo_small.png" />
<span class="logo__title">swagger</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>
其实我们只添加了两行代码:
<!--国际化操作:选择中文版 -->
<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>
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",
"Request Headers":"请求头",
"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":"服务器返回"
});
你也可以自己定义中文名称啦。
到此,Swagger 的汉化工作已经做完,是不是很简单,重新启动项目,访问 http://localhost/swagger-ui.html 就可以看到文档已经完全汉化啦:
小结:
好了,关于 Swagger2 在项目中的使用介绍就到这里。
源码下载:https://github.com/itguang/swagger/tree/master/springboot-swagger-demo1
全文完
以下文章您可能也会感兴趣:
你真的懂 Builder 设计模式吗?论如何实现真正安全的 Builder 模式
锁优化的简单思路
数据库索引优化
缓存的那些事
可线性化检查:与 NP 完全问题做斗争
Java 类型系统从入门到放弃
使用 RabbitMQ 实现 RPC
原来你是这样的 Stream —— 浅析 Java Stream 实现原理
分布式锁实践之一:基于 Redis 的实现
数据介绍一个 MySQL 自动化运维利器 - Inception
我们正在招聘 Java 工程师,欢迎有兴趣的同学投递简历到 rd-hr@xingren.com 。