# :-: **微服务网关聚合Swagger文档**
:-: \*\*\*\*
>[success] 微服务项目中,没有做API文档聚合,访问每个服务的API文档都需要访问单独的\`swagger-ui.html\`页面,既然我们使用了微服务,就应该有统一的API文档入口,\`knife4j\`有这方面的支持,本文将详细介绍其实现,希望对大家有所帮助!
> 采用Nacos作为注册中心,Gateway作为网关,使用`knife4j`来生成API文档。
相关服务划分:
* hjmall-gateway:网关服务,作为微服务API文档的访问入口,聚合所有API文档,需要引入文档前端UI包;
* hjmall-accout:用户服务,普通API服务,不需要引入文档前端UI包;
* hjmall-market:营销服务,普通API服务,不需要引入文档前端UI包。
## 具体实现
>[danger] 下面详细介绍下Spring Cloud Gateway + knife4j 聚合API文档的具体实现,依次搭建用户服务、营销服务和网关服务
每个服务引入如下jar包
~~~
<!--swagger2:API 文档生成工具 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
~~~
* hjmall-accout
>[danger] 我们首先来搭建用户服务,一个普通的API服务,很简单,仅需三步即可集成knife4j。
* 在`pom.xml`中添加相关依赖,一个SpringBoot的web功能依赖,knife4j的微服务依赖(不包含API文档的前端UI包);
~~~
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
</dependency>
~~~
>[danger] 添加Swagger相关配置,非常常规的配置,添加`@EnableKnife4j`注解开启knife4j的增强功能。
~~~
Swagger2Config 配置如下
~~~
~~~
package com.hjf.market.core;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.context.annotation.Bean;
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.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
@EnableKnife4j
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.hjf.market.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("营销系统接口Api")
.description("营销系统接口Api")
.termsOfServiceUrl("")
.contact("devteam")
.version("1.0")
.build();
}
}
~~~
>[danger] 我们接下来搭建营销服务,一个普通的API服务,直接参考上面用户服务的搭建即可。
>[danger] 最后我们搭建网关服务,作为微服务API文档的的统一入口,
> 聚合所有微服务的API文档。在`pom.xml`中添加相关依赖,
> Gateway相关依赖和knife4j的Starter(包含API文档的前端UI包);
~~~
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
~~~
>[danger] 在网关上添加Swagger资源配置,用于聚合其他微服务中Swagger的`api-docs`访问路径;
> SwaggerResourceConfig 配置如下
~~~
package com.hjf.gateway.swagger;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger资源配置
*/
@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
route.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("**", "v2/api-docs"))));
});
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
log.info("name:{},location:{}",name,location);
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
~~~
什么是Swagger的`api-docs`访问路径?该路径会返回JSON格式数据,Swagger渲染API文档页面的所有数据就是来源于此,比如我们的用户服务会返回如下信息,访问地址:[http://localhost:9000/api/account/v2/api-docs](http://localhost:9000/api/account/v2/api-docs)
![](https://img.kancloud.cn/1c/44/1c443af00848dce15dbf28a56d47e307_775x430.png)
>[danger] 接下来我们需要自定义Swagger各个配置的节点,简单来说就是自定义Swagger内部的各个获取数据的接口;
> SwaggerHandler配置
~~~
package com.hjf.gateway.swagger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import springfox.documentation.swagger.web.*;
import java.util.Optional;
@RestController
public class SwaggerHandler {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/swagger-resources/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
~~~
比如说`swagger-resources`这个接口,可用于获取所有微服务的`api-docs`访问路径,获取信息如下,访问地址:[http://localhost:9000](http://localhost:9000)[/swagger-resources](http://localhost:9201/swagger-resources)
![](https://img.kancloud.cn/ff/bc/ffbcdb0f3f8bb8b679b9af7e6ff89080_743x479.png)
## 功能演示
>[danger] 接下来我们来演示下微服务API文档聚合的功能,仅需要访问网关的API文档页面即可,可自行切换到相关服务的API文档。
> 在此之前先启动我们的Nacos注册中心,然后依次启动`hjmall-gateway、hjmall-accout、hjmall-market`服务;
> 访问 localhost:9000/doc.html
![](https://img.kancloud.cn/53/73/5373c11c8de9d4f758b40320a72fdaca_708x420.png)
## 总结
>[danger] 对比knife4j和原生Swagger的微服务使用,再次证明knife4j是springfox-swagger的增强UI实现,完全遵循了springfox-swagger中的使用方式。
> 官方文档:[https://doc.xiaominfo.com/guide/ui-front-gateway.html](https://doc.xiaominfo.com/guide/ui-front-gateway.html)
API详细说明
| 作用范围 | API | 使用位置 |
| --- | --- | --- |
| 协议集描述 | @Api | 用于controller类上 |
| 对象属性 | @ApiModelProperty | 用在出入参数对象的字段上 |
| 协议描述 | @ApiOperation | 用在controller的方法上 |
| Response集 | @ApiResponses | 用在controller的方法上 |
| Response | @ApiResponse | 用在 @ApiResponses里边 |
| 非对象参数集 | @ApiImplicitParams | 用在controller的方法上 |
| 非对象参数描述 | @ApiImplicitParam | 用在@ApiImplicitParams的方法里边 |
| 描述返回对象的意义 | @ApiModel | 用在返回对象类上 |
`api`标记,用在类上,说明该类的作用。可以标记一个`Controller`类做为`Swagger`文档资源,使用方式:
~~~
@Api(value = "/user", description = "用户管理")
~~~
与`Controller`注解并列使用。 属性配置:
| 属性名称 | 备注 |
| --- | --- |
| value | url的路径值 |
| tags | 如果设置这个值、value的值会被覆盖 |
| description | 对api资源的描述 |
| basePath | 基本路径可以不配置 |
| position | 如果配置多个Api 想改变显示的顺序位置 |
| produces | For example, "application/json, application/xml" |
| consumes | For example, "application/json, application/xml" |
| protocols | Possible values: http, https, ws, wss. |
| authorizations | 高级特性认证时配置 |
| hidden | 配置为true 将在文档中隐藏 |
`ApiOperation`标记,用在方法上,说明方法的作用,每一个`url`资源的定义,使用方式:
~~~
@ApiOperation("获取用户信息")
~~~
与`Controller`中的方法并列使用,属性配置:
| 属性名称 | 备注 |
| --- | --- |
| value | url的路径值 |
| tags | 如果设置这个值、value的值会被覆盖 |
| description | 对api资源的描述 |
| basePath | 基本路径可以不配置 |
| position | 如果配置多个Api 想改变显示的顺序位置 |
| produces | For example, "application/json, application/xml" |
| consumes | For example, "application/json, application/xml" |
| protocols | Possible values: http, https, ws, wss. |
| authorizations | 高级特性认证时配置 |
| hidden | 配置为true将在文档中隐藏 |
| response | 返回的对象 |
| responseContainer | 这些对象是有效的 "List", "Set" or "Map".,其他无效 |
| httpMethod | "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" and "PATCH" |
| code | http的状态码 默认 200 |
| extensions | 扩展属性 |
`ApiParam`标记,请求属性,使用方式:
~~~
public TableDataInfo list(@ApiParam(value = "查询用户列表", required = true)User user)
~~~
与Controller中的方法并列使用,属性配置:
| 属性名称 | 备注 |
| --- | --- |
| name | 属性名称 |
| value | 属性值 |
| defaultValue | 默认属性值 |
| allowableValues | 可以不配置 |
| required | 是否属性必填 |
| access | 不过多描述 |
| allowMultiple | 默认为false |
| hidden | 隐藏该属性 |
| example | 举例子 |
`ApiResponse`标记,响应配置,使用方式:
~~~
@ApiResponse(code = 400, message = "查询用户失败")
~~~
与`Controller`中的方法并列使用,属性配置:
| 属性名称 | 备注 |
| --- | --- |
| code | http的状态码 |
| message | 描述 |
| response | 默认响应类 Void |
| reference | 参考ApiOperation中配置 |
| responseHeaders | 参考 ResponseHeader 属性配置说明 |
| responseContainer | 参考ApiOperation中配置 |
`ApiResponses`标记,响应集配置,使用方式:
~~~
@ApiResponses({ @ApiResponse(code = 400, message = "无效的用户") })
~~~
与`Controller`中的方法并列使用,属性配置:
| 属性名称 | 备注 |
| --- | --- |
| value | 多个ApiResponse配置 |
`ResponseHeader`标记,响应头设置,使用方法
~~~
@ResponseHeader(name="head",description="响应头设计")
~~~
与`Controller`中的方法并列使用,属性配置:
| 属性名称 | 备注 |
| --- | --- |
| name | 响应头名称 |
| description | 描述 |
| response | 默认响应类 void |
| responseContainer | 参考ApiOperation中配置 |
- 项目介绍
- 项目声明
- 项目简介
- 架构设计
- 项目亮点功能介绍
- 技术栈介绍
- 核心功能
- 运行环境
- 项目更新日志
- 文档更新日志
- F&Q
- 部署教程
- 环境准备
- JDK安装
- JDK1.8,17共存
- maven
- 分布式缓存Redis
- 单机版
- 集群
- 注册&配置中心alibaba/nacos
- 介绍
- Nacos安装
- Nacos配置中心
- Nacos注册发现
- Nacos生产部署方案
- 服务监控-BootAdmin
- 基本介绍
- 如何使用
- 整合Admin-Ui
- 客户端配置
- 链路追踪
- 基本介绍
- SkyWalking-1
- Skywalking-1
- 消息队列
- Kafka
- docker安装kafka
- Linux集群
- Maven私服
- nexus安装部署
- nexus使用介绍
- 全文搜索elasticsearch
- windows集群搭建
- docker安装es
- ElasticHD
- linux集群部署
- 统一日志解决方案
- 日志解决方案设计
- 介绍与相关资料
- ELK安装部署
- elasticsearch 7.5
- logstash-7.5
- kibana-7.5
- filebeat
- 服务监控-Prometheus
- Prometheus安装配置
- Prometheus介绍
- grafana
- 持续集成部署CICD
- 自动化部署Jenkins
- 安装部署win
- 打包发布远程执行
- 安装部署linux
- jenkins+gitlab+docker容器化工程自动化部署
- Git
- CICD说明
- 阿里云效
- CentOS_MYSQL安装
- docker
- 安装
- Docker安装Nginx
- Docker部署启动springboot
- dockerCompose
- harbor
- Docker私有镜像仓库
- Portainer
- Docker远程连接设置
- 打包工程
- 必要启动模块
- 核心模块
- 登录认证
- 缓存功能
- 日志模块
- 分布式锁
- 消息队列
- 异常处理
- 系统接口
- 参数验证
- es检索
- 数据导出
- 系统设计
- 系统总体架构
- 扩展模块(可选)
- 限流熔断alibaba/sentinel
- 使用Sentinel实现gateway网关及服务接口限流
- Sentinel使用Nacos存储规则及同步
- 服务调用Feign
- Feign基本介绍
- 如何使用
- 负载均衡
- 请求超时
- 请求拦截器
- 分布式任务调度
- XXL-JOB
- 分布式事务
- TX-LCN
- Seata
- Seata原理解析
- 数据库分库分表
- swagger文档
- 分布式ID生成器解决方案
- 服务网关CloudGateway
- 基本介绍
- 使用网关
- 路由配置
- 全局过滤器
- 服务认证授权架构设计
- 认证服务流程
- 授权服务流程
- 系统幂等性设计与实践
- 分布式日志链路跟踪
- 实时搜索系统设计
- 应用性能
- 压力测试工具
- Apache JMeter介绍和安装
- ApacheJMeter使用
- JVM
- JVM性能调优
- 常见JVM内存错误及解决方案
- JVM 分析工具详解
- Spring Cloud性能调优
- Linux运维
- Linux 常用命令
- Linux开启端口