本文属于原创,转载注明出处,欢迎关注微信小程序 小白AI博客
微信公众号 小白AI
或者网站 https://xiaobaiai.net
[TOC]
在如今前后端分离开发的模式下,前端调用后端提供的API去实现数据的展示或者相关的数据操作,保证及时更新和完整的REST API文档将会大大地提高两边的工作效率,减少不必要的沟通成本。本文采用的Swagger2就是一个当前流行的通过少量的注解就可以生成漂亮的API文档工具,且在生成的在线文档中提供类似POSTMAN直接调试能力,不仅仅是静态的文档。接下来将会利用这个工具与Spring Boot项目结合,最终生成我们上一篇文章中所涉及到的REST API文档。
这一篇文章基本将Swagger2在生产环境中可能会用到的配置都有涉及,慢慢看吧,看了这一篇因该是够了。
Swagger是与用于实现 OpenAPI 文档广泛使用的工具,Swagger工具集包括开源工具,免费工具和商业工具的组合,可在API生命周期的不同阶段使用。
Swagger Editor
(开源):使用Swagger编辑器,可以在浏览器内的YAML文档中编辑OpenAPI规范并支持实时预览文档,可以参考官方的Demo https://editor.swagger.io/ Swagger UI
(开源):让Swagger产生的文档更漂亮,而且支持API交互操作,在生成文档后,直接在浏览器中浏览,并可以实现类似 curl
命令或者 postman
访问我们的API,并返回相关数据。 Swagger Codegen
(开源): 是一个代码生成器,可以通过Swagger API定义生成不同语言版本的服务端和客户端工程代码。 Swagger Core
(开源):用于生成Swagger API规范的示例和服务器集成,可轻松访问REST API,结合 Swagger UI
,让生成的文档更漂亮。 Swagger Parser
(开源): Java开发,解析OpenAPI定义的独立库 Swagger Inspector
(免费):API在线测试工具,验证API并从现有API生成OpenAPI定义功能 https://goo.gl/fZYHWz SwaggerHub
(免费和商用版):API设计和文档化,为使用OpenAPI的团队打造。 参考 《Spring Boot从零入门5_五脏俱全的RESTful Web Service构建》 。构建好后有如下REST API:
# 获取所有用户信息 GET http://localhost:8080/api/v1/users # 新增一个用户,参数通过body传递 POST http://localhost:8080/api/v1/users # 更新一个用户信息 PUT http://localhost:8080/api/v1/users/{id} # 删除指定用户 DELETE http://localhost:8080/api/v1/users/{id}
构建好RESTful WEB服务后,接下来我们集成Swagger,然后对上节中的REST API自动生成接口文档。
集成Swagger2,需要在pom.xml中添加依赖源:
<dependencies> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <!-- 截至2019年11月7日为止,最新版本为2.9.2 --> <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --> <version>2.9.2</version> </dependency> </dependencies>
springfox
有一个专用对象Docket,可以灵活的配置Swagger的各种属性,首先我们简单的创建一个Swagger配置类 Swagger2Config.java
:
@Configuration @EnableSwagger2 public class Swagger2Config { @Bean("UsersApis") public Docket usersApis() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); } }
这里的 @Configuration
注解用于定义配置类,被注解的类内部包含有一个或多个被 @Bean
注解的方法,这些方法将会被 AnnotationConfigApplicationContext
类进行扫描,并用于构建Bean定义,初始化对象。 @ComponentScan
会自动获取所有的Spring Components,包括 @Configuration
类。另外这里的“用户管理模块”API生成配置很简单,对所有路径上API都去生成文档。
当完成Swagger2的配置类时,启动WEB服务,通过 http://localhost:8080/v2/api-docs 就可以访问生成文档内容,但是浏览器返回的是JSON内容,基本上很难给需要用到相关API的开发人员进行参考。这个时候就需要用到 Swagger2 UI
了。
pom.xml添加依赖,然后重启WEB服务就可以了,再次访问 http://localhost:8080/swagger-ui.html ,这时候看到的就是WEB文档了。
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
从swagger-ui页面看到的内容有一部无关的内容,或者是如何明显表现跟项目相关的内容呢?下面章节详细讲解Swagger的各种配置,能够应用到实际生产环境中去。
首先,如果要将我们最后生成的API文档给生产环境的开发人员查阅,那么友好的展示信息和归类是很有必要的,我们接下来实现如下目标:
文档的各种信息说明
API分组
为了更好地展示API分组功能,这里另外加了一组REST API (代码层面上只需要将User相关的代码全部复制一份,将User关键字全部改为Product就可以了,包括大小写):
# 获取所有产品信息 GET http://localhost:8080/api/v1/products # 新增一个产品,参数通过body传递 POST http://localhost:8080/api/v1/products # 更新一个产品信息 PUT http://localhost:8080/api/v1/products/{id} # 删除指定产品 DELETE http://localhost:8080/api/v1/products/{id}
@Configuration @EnableSwagger2 public class Swagger2Config { @Bean("UsersApis") public Docket usersApis() { return new Docket(DocumentationType.SWAGGER_2) // select()返回的是ApiSelectorBuilder对象,而非Docket对象 .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) // build()返回的是Docket对象 .build() // 测试API时的主机URL .host("https://xiaobaiai.net") // API前缀 .pathProvider(new RelativePathProvider(null) { @Override public String getApplicationBasePath() { return "/prefix"; } }) .apiInfo(apiInfo()); } public ApiInfo apiInfo() { // API负责人的联系信息 final Contact contact = new Contact( "Ethan", "https://xiaobaiai.net", "ycm_hy@163.com"); return new ApiInfoBuilder() // API文档标题 .title("X系统平台接口文档") // API文档描述 .description("用户/产品相关API, 更多请关注公众号: 小白AI 或微信小程序:小白AI博客") // 服务条款URL .termsOfServiceUrl("https://github.com/yicm") // API文档版本 .version("1.0") // API负责人的联系信息 .contact(contact) // API的许可证Url .licenseUrl("http://license.coscl.org.cn/MulanPSL") .license("MulanPSL") .build(); } }
通过添加文档信息编译对象 ApiInfoBuilder
可以配置API文档的各种信息,包括标题、描述、服务条款、版本、责任人、许可证等。最后在Docket中添加信息配置对象即可生效。
上面的文档信息配置中默认是没有对API分组的,即所有的API都展示在了一个页面,没有隔离,如果需要分组,那我们需要对不同API组分配Bean,目前示例可以分为用户API组和产品API组,然后通过 apis()
和 paths()
进行API过滤。
为了不显示某个包下面API或某个URL路径下API, Docket
提供了 apis()
和 paths()
两 个方法来帮助我们在不同级别上过滤接口(上面示例我们默认对这两个设置是不做任何过滤,扫描所有API):
apis() paths()
apis和paths中的 Predicates
除了 any
、 ant
、 none
,还支持 regex
正则表达式。
如:
PathSelectors.regex("/api/v2/users.*")
下面就是分组示例代码,实现分组,很简单,就是在Docket中配置组名就好了:
@Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket usersApis() { return new Docket(DocumentationType.SWAGGER_2) .groupName("用户管理接口") // select()返回的是ApiSelectorBuilder对象,而非Docket对象 .select() .apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.user")) .paths(Predicates.or( // 两个**,可以匹配底下所有URL // 一个*,只能匹配一级URL分段 PathSelectors.ant("/api/v1/users/**"), PathSelectors.ant("/api/v1/users/*"))) // build()返回的是Docket对象 .build() // 测试API时的主机URL .host("https://xiaobaiai.net") // API前缀,最终所有API的基础地址就是host+prefix: https://xiaobaiai.net/prefix .pathProvider(new RelativePathProvider(null) { @Override public String getApplicationBasePath() { return "/prefix"; } }) .apiInfo(apiInfo()); } @Bean public Docket productsApis() { return new Docket(DocumentationType.SWAGGER_2) .groupName("产品管理接口") // select()返回的是ApiSelectorBuilder对象,而非Docket对象 .select() .apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.product")) .paths(Predicates.or( // 两个**,可以匹配底下所有URL // 一个*,只能匹配一级URL分段 PathSelectors.ant("/api/v1/products/**"), PathSelectors.ant("/api/v1/products/*"))) // build()返回的是Docket对象 .build() // 测试API时的主机URL .host("https://xiaobaiai.net") // API前缀 .pathProvider(new RelativePathProvider(null) { @Override public String getApplicationBasePath() { return "/prefix"; } }) .apiInfo(apiInfo()); } public ApiInfo apiInfo() { // API负责人的联系信息 final Contact contact = new Contact( "Ethan", "https://xiaobaiai.net", "ycm_hy@163.com"); return new ApiInfoBuilder() // API文档标题 .title("X系统平台接口文档") // API文档描述 .description("用户/产品相关API, 更多请关注公众号: 小白AI 或微信小程序:小白AI博客") // 服务条款URL .termsOfServiceUrl("https://github.com/yicm") // API文档版本 .version("1.0") // API负责人的联系信息 .contact(contact) // API的许可证Url .licenseUrl("http://license.coscl.org.cn/MulanPSL") .license("MulanPSL") .build(); } }
分组配置完成后,重新启动,打开浏览器就可以看到效果了:
虽然上面我们已经可以控制API的显示和分组了,但是对于API一些更详细,对组内API再次归类之类的,比如小组的描述信息,以及每个API如何去控制它的参数说明,返回值说明等。这些都是通过注解去实现的,接下来我们讲述常用的注解及作用:
@Api
: 将这个注解添加到控制器类上,则可以给控制器添加描述类信息: 相关可设置参数有:
示例:
// Swagger配置类 @Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket productsApis() { return new Docket(DocumentationType.SWAGGER_2) .groupName("产品管理接口") .select() .apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.product")) .paths(Predicates.or( PathSelectors.ant("/api/v1/products/**"), PathSelectors.ant("/api/v1/products/*"))) .build() .host("https://xiaobaiai.net") .pathProvider(new RelativePathProvider(null) { @Override public String getApplicationBasePath() { return "/prefix"; } }) .apiInfo(apiInfo()) .tags(new Tag("产品操作分组1", "产品查询相关操作."), new Tag("产品操作分组2", "产品添加或删除相关操作."), new Tag("产品操作分组3", "产品更新相关操作."), new Tag("产品操作分组4", "产品相关全部操作.")); } }
// 控制器类 @RestController @RequestMapping("/api/v1") @Api(tags={"产品接口文档列表"}) public class ProductServiceController { ... }
效果如下:
@ApiIgnore
: 作用在REST API控制器 方法
上,则该API不会被显示出来: @ApiIgnore @RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE) public ResponseEntity<Object> delete(@PathVariable("id") String id) { ... }
@ApiOperation
注解用于控制器 方法
上面,用于对方法的描述,相关参数设置描述如下: 示例:
@ApiOperation(value = "获取所有产品", notes = "每调用一次,就耗费流量100M", response = String.class) @GetMapping(value = "/products") public ResponseEntity<Object> getProduct() { return new ResponseEntity<>(productService.getProducts(), HttpStatus.OK); }
最后效果就是:
@ApiImplicitParams
和 @ApiImplicitParam
注解用于控制器方法传入参数的说明。默认情况下,Swagger会根据API方法中的传入参数进行参数说明的生成,不过参数说明默认就是变量名,因为这两个注解不一定需要。相关参数设置说明如下: paramType:参数传递类型
@RequestHeader @RequestParam @PathVariable @RequestBody
示例:
// 如果只有一个参数,则仅仅@ApiImplicitParam就可以了 @ApiImplicitParams({ @ApiImplicitParam(name="id", value="产品ID值", required = true), @ApiImplicitParam(name="product", value="产品内容", required = true) }) @RequestMapping(value = "/products/{id}", method = RequestMethod.PUT) public ResponseEntity<Object> updateProduct(@PathVariable("id") String id, @RequestBody Product product) { productService.updateProduct(id, product); return new ResponseEntity<>("Product is updated successsfully", HttpStatus.OK); }
@ApiParam
: 作用同ApiImplicitParam,单个参数描述一般常用该注解,而且该注解只能与 JAX-RS 1.x/2.x 注解结合使用。参数设置说明如下: @ApiResponses
、 @ApiResponse
: 用于控制器方法返回值的说明,参数设置说明如下: 示例:
@ApiOperation(value = "获取所有产品", notes = "每调用一次,就耗费流量100M",response =Product.class, responseContainer="List") @ApiResponses({ @ApiResponse(code = 200, message = "成功!", response=Product.class), @ApiResponse(code = 401, message = "未授权!", response=Product.class), @ApiResponse(code = 404, message = "页面未找到!", response=Product.class), @ApiResponse(code = 403, message = "出错了!", response=Product.class) }) @GetMapping(value = "/products") public ResponseEntity<Object> getProduct() { return new ResponseEntity<>(productService.getProducts(), HttpStatus.OK); }
效果如下:
@Deprecated @ApiModel
示例:
@ApiModel(value="Product",description="对产品定义的描述") public class Product { ... }
@ApiModelProperty
: 同样用于在JavaBean类的属性上面,说明相关属性。类似于方法上说明的 @ApiImplicitParam
。设置参数有: 管理不同API版本有好几种方式:
/api/v1/users /api/users?version=1 curl -H "Accept: application/vnd.piomin.v1+json" http://localhost:8080/api/users
这里我们对第一种方式示例展示:
在Swagger2Config.java中新添加一个用户API Docket:
@Configuration @EnableSwagger2 public class Swagger2Config { @Bean("UsersApis_V1") public Docket usersApisV1() { return new Docket(DocumentationType.SWAGGER_2) .groupName("用户管理接口V1") .select() .apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.user")) .paths(Predicates.or( // 过滤版本v1 PathSelectors.ant("/api/v1/users/**"), PathSelectors.ant("/api/v1/users/*"))) .build() .host("https://xiaobaiai.net") .pathProvider(new RelativePathProvider(null) { @Override public String getApplicationBasePath() { return "/prefix"; } }) .apiInfo(apiInfo()); } @Bean("UsersApis_V21") public Docket usersApisV2() { return new Docket(DocumentationType.SWAGGER_2) .groupName("用户管理接口V2") .select() .apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.user")) .paths(Predicates.or( // 过滤版本v1 PathSelectors.ant("/api/v2/users/**"), PathSelectors.ant("/api/v2/users/*"))) .build() .host("https://xiaobaiai.net") .pathProvider(new RelativePathProvider(null) { @Override public String getApplicationBasePath() { return "/prefix"; } }) .apiInfo(apiInfo()); } @Bean public Docket productsApis() { return new Docket(DocumentationType.SWAGGER_2) ..... } public ApiInfo apiInfo() { final Contact contact = new Contact( "Ethan", "https://xiaobaiai.net", "ycm_hy@163.com"); return new ApiInfoBuilder() .title("X系统平台接口文档") .description("用户/产品相关API, 更多请关注公众号: 小白AI 或微信小程序:小白AI博客") .termsOfServiceUrl("https://github.com/yicm") .version("1.0") .contact(contact) .licenseUrl("http://license.coscl.org.cn/MulanPSL") .license("MulanPSL") .build(); } }
然后控制器添加新版本API方法:
@RestController @RequestMapping("/api") public class UserServiceController { @Autowired UserService userService; @DeleteMapping({"/v1/users/{id}", "/v2/users/{id}"}) public ResponseEntity<Object> delete(@PathVariable("id") String id) { userService.deleteUser(id); return new ResponseEntity<>("User is deleted successsfully", HttpStatus.OK); } @PutMapping({"/v1/users/{id}", "/v2/users/{id}"}) public ResponseEntity<Object> updateUser(@PathVariable("id") String id, @RequestBody User user) { userService.updateUser(id, user); return new ResponseEntity<>("User is updated successsfully", HttpStatus.OK); } @PostMapping({"/v1/users", "/v2/users"}) public ResponseEntity<Object> createUser(@RequestBody User user) { userService.createUser(user); return new ResponseEntity<>("User is created successfully", HttpStatus.CREATED); } @GetMapping({"/v1/users"}) @Deprecated public ResponseEntity<Object> getUser() { return new ResponseEntity<>(userService.getUsers(), HttpStatus.OK); } @GetMapping(value = "/v2/users") public ResponseEntity<Object> getUser(@RequestParam String id) { return new ResponseEntity<>(userService.getUsers(), HttpStatus.OK); } }
最后效果:
其他的方式类似也差不多,如在Header中区分版本,这里就不展开了。
当我们的REST API加入的授权机制时,即需具有对该API的访问权限,才能够操作该API,但是我们想在Swagger UI中去调试API,那么如何解决每个API一次性授权,全部API可访问呢?增加使用的方便性,不用每次都对每个API进行授权。不过需要在WEB服务中已经使用了API授权机制才会需要这项配置。这里暂不展开,后面单独讲述 Spring Security
+ Swagger2 UI
配置。
应该是不能的: https://github.com/swagger-ap...
这一篇从介绍Swagger2入手,讲述在Spring Boot中如何集成和配置Swagger2,并生成生成环境中的在线API文档,包括如何将API分组,组信息描述,API信息描述,API方法参数描述,如果对API版本进行管理等,最后还扩展了内容,包括如何为每个API配置全局Token等。内容很全,参考这一篇应该是够了,继续!
本文属于原创,转载注明出处,欢迎关注CSDN freeape 或微信小程序 小白AI博客
微信公众号 小白AI
或者网站 https://xiaobaiai.net