随着前后端分离架构和微服务架构的流行,我们使用Spring Boot来构建RESTful API项目的场景越来越多。通常我们的一个RESTful API就有可能要服务于多个不同的开发人员或开发团队:IOS开发、Android开发、Web开发甚至其他的后端服务等。为了减少与其他团队平时开发期间的频繁沟通成本,传统做法就是创建一份RESTful API文档来记录所有接口细节,然而这样的做法有以下几个问题:
由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳;随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象;为了解决上面这样的问题,Swagger2应运而生,通过将Swagger2整合到我们的Spring Boot应用中,我们可以快速的组织出强大RESTful API文档,Swagger2大量的减少了我们创建文档的工作量,同时通过简单的Annotation将API说明内容整合入实现代码中,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明,进一步减少了我们维护文档的成本。
本文将要介绍的是一个GitHub上的一个开源项目spring-boot-starter-swagger。该项目主要利用Spring Boot的自动化配置特性,对Swagger2进行了进一步的封装,更加简化了我们在Spring Boot应用中整合Swagger2的步骤。
Swagger2是一款业界比较流行的实现RESTful API的文档在线自动生成及RESTful API在线调试的工具。优点有:
Swagger2可以轻松的整合到Spring Boot工程中,并与Spring MVC程序配合组织出强大RestFul接口文档,通过在项目中引入Swagger,可以使用简单的Annotation,就实现了接口文档化;Swagger2既可以减少我们创建文档的工作量,同时说明内容又整合入实现代码中,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明;Swagger2提供标准的json或yaml文档,方便做进一步解析,典型应用是接口自动化测试;Swagger2也提供了强大的页面测试功能来调试每个RESTful API,Swagger2页面可以直接进行测试(try-it-out功能,部分替代Postman);Swagger还提供类似于github的SwaggerHub,相当于公共的API文档集散地Swagger2项目主页:https://swagger.io/
spring-boot-starter-swagger是一个GitHub上的一个开源项目,该项目主要利用Spring Boot的自动化配置特性来实现快速的将swagger2引入spring boot应用来生成API文档,简化原生使用swagger2的整合代码。
源码地址 GitHub:https://github.com/dyc87112/spring-boot-starter-swagger码云:https://gitee.com/didispace/spring-boot-starter-swagger Demo地址:https://github.com/dyc87112/swagger-starter-demo说明
1. 当前最新版本 1.9.0.RELEASE;
2. 从1.6.0版本开始,artifactId修改为swagger-spring-boot-starter,1.6.0之前的版本不做修改,依然为spring-boot-starter-swagger;
在应用主类中增加@EnableSwagger2Doc注解 /** * * @ClassName: DemoApplication * @Description: Demo应用启动类 * @author YuanXu * @date 2019年10月31日 上午10:53:23 * */ @EnableSwagger2Doc @SpringBootApplication public class DemoApplication { /** * @Title main * @Description 测试应用启动入口 * @author YuanXu * @param args */ public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }默认情况下就能产生所有当前Spring MVC加载的请求映射文档。
swagger.base-path和swagger.exclude-path使用ANT规则配置。
我们可以使用swagger.base-path来指定所有需要生成文档的请求路径基础规则,然后再利用swagger.exclude-path来剔除部分我们不需要的。
例如:
management: context-path: /ops swagger: base-path: /** exclude-path: /ops/**, /error上面的设置将解析所有除了/ops/开始以及spring boot自带/error请求路径。
其中,exclude-path可以配合management.context-path=/ops设置的spring boot actuator的context-path来排除所有监控端点。
当我们一个项目的API非常多的时候,我们希望对API文档实现分组。从1.2.0.RELEASE开始,将支持分组配置功能,具体配置内容如下:
swagger: docket: <name>: base-package: swagger扫描的基础包,默认:全扫描 base-path: 需要处理的基础URL规则,默认:/** contact: email: 维护人email name: 维护人 url: 维护人URL description: 描述 exclude-path: 需要排除的URL规则,默认:空 globalOperationParameters: - description: 描述信息 modelRef: 指定参数存放位置,可选header,query,path,body.form name: 参数名 parameterType: 指定参数是否必传,true,false license: 许可证 licenseUrl: 许可证URL modelRef: 指定参数类型 name: 参数名 parameterType: 指定参数存放位置,可选header,query,path,body.form required: true=指定参数是否必传,true,false termsOfServiceUrl: 服务条款URL title: 标题 version: 版本<name>为swagger文档的分组名称,同一个项目中可以配置多个分组,用来划分不同的API文档。
默认配置与分组配置可以一起使用。在分组配置中没有配置的内容将使用默认配置替代,所以默认配置可以作为分组配置公共部分属性的配置。swagger.docket.aaa.globalOperationParameters[0].name会覆盖同名的全局配置。
像每个接口都需要鉴权这种参数,可以在配置文件中统一定义,这样省去每个接口再写的麻烦,也能兼顾页面的测试。
swagger: global-operation-parameters: - name: TOKEN description: 鉴权 modelRef: string parameterType: header required: true # 公共参数写成requierd, 对于不需要登录的接口随便写一个字符串即可基础配置
swagger: ignored-parameter-types: - com.didispace.demo.User - com.didispace.demo.Product分组配置
swagger: group-a: ignored-parameter-types: - com.didispace.demo.User - com.didispace.demo.Product支持 POST,GET,PUT,PATCH,DELETE,HEAD,OPTIONS,TRACE 全局响应消息配置,具体配置内容如下:
swagger: apply-default-response-messages: false # 取消使用默认预定义的响应消息,并使用自定义响应消息 global-response-message: get: - code: 401 message: 401get - code: 500 message: 500get modelRef: ERROR post: - code: 500 message: 500post modelRef: ERROR调试按钮的控制(try it out)
swagger: ui-config: submit-methods: get,delete该参数值为提供调试按钮的HTTP请求类型,多个用逗号分割,如果不想开启调试功能,只需要如下设置即可:
swagger: ui-config: submit-methods:其他配置
swagger: ui-config: json-editor: false # json编辑器 request-timeout: 5000 # 页面调试请求的超时时间 show-request-headers: true # 显示请求头更多配置说明见官方说明:https://github.com/SpringForAll/spring-boot-starter-swagger
在整合完Swagger之后,在http://localhost:8080/swagger-ui.html页面中可以看到,关于各个接口的描述还都是英文或遵循代码定义的名称产生的。这些内容对用户并不友好,所以我们需要自己增加一些说明来丰富文档内容。我们通过@Api,@ApiOperation注解来给API增加说明、通过@ApiImplicitParam、@ApiModel、@ApiModelProperty注解来给参数增加说明。 如下所示:
UserController.java 类
/** * * @ClassName: UserController * @Description: 用户管理Controller * @author YuanXu * @date 2019年10月31日 下午4:49:00 * */ @Api(tags = "用户管理") @RestController @RequestMapping(value = "/users") // 通过这里配置使下面的映射都在/users下 public class UserController { /** * 创建线程安全的Map,模拟users信息的存储 */ static Map<Long, User> users = Collections.synchronizedMap(new HashMap<>()); @GetMapping("/") @ApiOperation(value = "获取用户列表") public List<User> getUserList() { List<User> r = new ArrayList<>(users.values()); return r; } @PostMapping("/") @ApiOperation(value = "创建用户", notes = "根据User对象创建用户") public String postUser(@RequestBody User user) { users.put(user.getId(), user); return "success"; } @GetMapping("/{id}") @ApiOperation(value = "获取用户详细信息", notes = "根据url的id来获取用户详细信息") public User getUser(@PathVariable Long id) { return users.get(id); } @PutMapping("/{id}") @ApiImplicitParam(paramType = "path", dataType = "Long", name = "id", value = "用户编号", required = true, example = "1") @ApiOperation(value = "更新用户详细信息", notes = "根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息") public String putUser(@PathVariable Long id, @RequestBody User user) { User u = users.get(id); u.setName(user.getName()); u.setAge(user.getAge()); users.put(id, u); return "success"; } @DeleteMapping("/{id}") @ApiOperation(value = "删除用户", notes = "根据url的id来指定删除对象") public String deleteUser(@PathVariable Long id) { users.remove(id); return "success"; } }User.java 类
/** * * @ClassName: User * @Description: 用户 实体 * @author YuanXu * @date 2019年10月31日 下午4:53:23 * */ @Data @ApiModel(description = "用户实体") public class User { @ApiModelProperty("用户编号") private Long id; @ApiModelProperty("用户姓名") private String name; @ApiModelProperty("用户年龄") private Integer age; }完成上述代码添加后,启动Spring Boot程序,访问:http://localhost:8080/swagger-ui.html,就能看到中文说明的文档了。
打开API文档页面地址:http://localhost:8080/swagger-ui.html
API文档JSON数据获取地址:http://localhost:8080/v2/api-docs
Zuul工程pom.xml引入依赖
<!-- 引入 springboot parent ,帮我们实现了很多jar包的依赖管理 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- 引入spring-cloud-starter-zuul的依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <!-- 引入spring-cloud-starter-netflix-eureka-client的依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 引入swagger-spring-boot-starter的依赖 --> <dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.9.0.RELEASE</version> </dependency> </dependencies>添加DocumentationConfig.java类
通过遍历eureka路由方式自动添加所有微服务 API 文档,SwaggerResourcesProvider 是资源提供者,我们重写他,把各个微服务的API文档资源路径返回,注释部分为手动添加的方式。
/** * * @ClassName: DocumentationConfig * @Description: Zuul整合Swagger2汇总API接口文档 * @author YuanXu * @date 2019年11月1日 上午10:30:37 * */ @Primary @Component public class DocumentationConfig implements SwaggerResourcesProvider { private final RouteLocator routeLocator; public DocumentationConfig(RouteLocator routeLocator) { this.routeLocator = routeLocator; } /* * (非 Javadoc) * <p>Title: get </p> * <p>遍历eureka路由方式自动添加所有微服务API文档</p> * <p>Author: YuanXu </p> * @return * @see com.google.common.base.Supplier#get() */ @Override public List<SwaggerResource> get() { List<SwaggerResource> resources = new ArrayList<SwaggerResource>(); List<Route> routes = routeLocator.getRoutes(); routes.forEach(route -> { resources.add(swaggerResource(route.getId(), route.getFullPath().replace("**", "v2/api-docs"), "1.0")); }); return null; } /* * (非 Javadoc) * <p>Title: get </p> * <p>手动添加微服务API文档</p> * <p>Author: YuanXu </p> * @return * @see com.google.common.base.Supplier#get() */ /* * @Override * public List<SwaggerResource> get() { * List resources = new ArrayList<>(); * resources.add(swaggerResource("基础服务API文档", "/yx-base-service/v2/api-docs", "1.0")); * resources.add(swaggerResource("文件服务API文档", "/yx-file-service/v2/api-docs", "1.0")); * return resources; * } */ private SwaggerResource swaggerResource(String name, String location, String version) { SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setLocation(location); swaggerResource.setSwaggerVersion(version); return swaggerResource; } }查看API整合效果 运行相关服务和zuul网关服务
浏览器输入:http://localhost:8090/swagger-ui.html可查看API文档整合后的效果