网关层聚合Swagger接口文档编码

由于各个微服务实例都已经聚合了 Swagger 接口文档且一切功能正常,因此这里不再赘述。下面以商城端网关为例,直接讲解如何在网关层聚合对应的微服务实例 Swagger 接口文档。

第一步,添加 Swagger3 依赖。

打开商城端网关 newbee-mall-cloud-gateway-mall 项目下的 pom.xml 文件,增加 Swagger3 依赖,新增代码如下:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
</dependency>

第二步,增加专门的路由配置来处理 Swagger 接口文档聚合。

网上关于 “Spring Cloud Gateway 聚合 Swagger 接口文档” 这个知识点的教程挺多,但是大部分是小案例,聚合时也是将配置文件中的所有路由配置都读取并做聚合。具体的业务实现中会有些不同,如当前项目中会对一个服务做两个路由配置,代码如下:

# 分类接口的路由配置
spring.cloud.gateway.routes[2].id=goods-service-route
spring.cloud.gateway.routes[2].uri=lb://newbee-mall-cloud-goods-service
spring.cloud.gateway.routes[2].order=1
spring.cloud.gateway.routes[2].predicates[0]=Path=/categories/mall/**

# 商品接口的路由配置
spring.cloud.gateway.routes[3].id=goods-service-route2
spring.cloud.gateway.routes[3].uri=lb://newbee-mall-cloud-goods-service
spring.cloud.gateway.routes[3].order=1
spring.cloud.gateway.routes[3].predicates[0]=Path=/goods/mall/**

其实这两个路由配置都指向商品微服务,如果读取所有路由,那么在网关层 Swagger UI 页面中就有重复的 Swagger 资源。因此,笔者在这里做了一些调整,增加了几条针对 Swagger 接口文档聚合的路由配置,在 newbee-mall-cloud-gateway-mall 项目下的 application.properties 文件中增加如下内容:

# 商品微服务 Swagger 接口文档的路由配置
spring.cloud.gateway.routes[7].id=goods-service-swagger-route
spring.cloud.gateway.routes[7].uri=lb://newbee-mall-cloud-goods-service
spring.cloud.gateway.routes[7].order=1
spring.cloud.gateway.routes[7].predicates[0]=Path=/goods/swagger/**
spring.cloud.gateway.routes[7].filters[0]=StripPrefix=2
# 推荐微服务 Swagger 接口文档的路由配置
spring.cloud.gateway.routes[8].id=recommend-service-swagger-route
spring.cloud.gateway.routes[8].uri=lb://newbee-mall-cloud-recommend-service
spring.cloud.gateway.routes[8].order=1
spring.cloud.gateway.routes[8].predicates[0]=Path=/indexConfigs/swagger/**
spring.cloud.gateway.routes[8].filters[0]=StripPrefix=2
# 订单微服务 Swagger 接口文档的路由配置
spring.cloud.gateway.routes[9].id=order-service-swagger-route
spring.cloud.gateway.routes[9].uri=lb://newbee-mall-cloud-order-service
spring.cloud.gateway.routes[9].order=1
spring.cloud.gateway.routes[9].predicates[0]=Path=/orders/swagger/**
spring.cloud.gateway.routes[9].filters[0]=StripPrefix=2
# 用户微服务 Swagger 接口文档的路由配置
spring.cloud.gateway.routes[10].id=user-service-swagger-route
spring.cloud.gateway.routes[10].uri=lb://newbee-mall-cloud-user-service
spring.cloud.gateway.routes[10].order=1
spring.cloud.gateway.routes[10].predicates[0]=Path=/users/swagger/**
spring.cloud.gateway.routes[10].filters[0]=StripPrefix=2
# 购物车微服务 Swagger 接口文档的路由配置
spring.cloud.gateway.routes[11].id=shop-cart-service-swagger-route
spring.cloud.gateway.routes[11].uri=lb://newbee-mall-cloud-shop-cart-service
spring.cloud.gateway.routes[11].order=1
spring.cloud.gateway.routes[11].predicates[0]=Path=/carts/swagger/**
spring.cloud.gateway.routes[11].filters[0]=StripPrefix=2

第三步,增加聚合配置类。

ltd.gateway.cloud.newbee.config 包下新建 PolymerizeSwaggerProvider 配置类,重写 get() 方法,代码如下:

package ltd.gateway.cloud.newbee.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.config.GatewayProperties;
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 资源
 */
@Primary
@Component
public class PolymerizeSwaggerProvider implements SwaggerResourcesProvider {

    /**
     * Swagger Doc 的 URL 后缀
     */
    public static final String API_DOCS_URL = "/v3/api-docs";

    @Autowired
    private GatewayProperties gatewayProperties;

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        //需要聚合的服务路由配置
        routes.add("user-service-swagger-route");
        routes.add("recommend-service-swagger-route");
        routes.add("goods-service-swagger-route");
        routes.add("order-service-swagger-route");
        routes.add("shop-cart-service-swagger-route");
        gatewayProperties.getRoutes().stream().filter(routeDefinition ->
                routes.contains(routeDefinition.getId())).forEach(routeDefinition -> routeDefinition.getPredicates().stream()
                .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                .forEach(predicateDefinition -> {
                    resources.add (swaggerResource(routeDefinition.getId(),
                            predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
                                    .replace("/**", API_DOCS_URL)));
                    return resources;
                });
    }

    private SwaggerResource swaggerResource(String name, String url) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(url);
        swaggerResource.setSwaggerVersion("3.0");
        return swaggerResource;
    }
}

这样,网关层 Swagger UI 页面在加载时请求的 /swagger-resources 得到的就不再是一条数据,而是上述代码中根据 5 条路由信息整合后的 5 条 Swagger 资源了。更通俗一些理解,就是将 http://localhost:29000/v3/api-docs、http://localhost:29010/v3/api-docs、http://localhost:29020/v3/api-docs、http://localhost:29030/v3/api-docs、http://localhost:29040/v3/api-docs 这 5 个 URL 组装到 Swagger 资源列表中。

第四步,网关层过滤器对 Swagger 资源聚合请求放行。

由于网关层都做了全局拦截器,因此对获取具体的 Swagger 接口信息的请求要放行,否则这些请求无法正常读取到数据。

ValidMallUserTokenGlobalFilter 类下的 filter() 方法中增加如下代码:

ignoreURLs.add("/indexConfigs/swagger/v3/api-docs");
ignoreURLs.add("/carts/swagger/v3/api-docs");
ignoreURLs.add("/orders/swagger/v3/api-docs");
ignoreURLs.add("/users/swagger/v3/api-docs");
ignoreURLs.add("/goods/swagger/v3/api-docs");

后台管理系统网关中聚合 Swagger 资源的编码步骤与此一致,这里不再赘述,读者可以直接下载本节源代码查看和学习。

最后来看一下实际效果。依次启动各个微服务架构项目及商城端网关,在浏览器中输入网关层 Swagger UI 页面的网址: http://localhost:29110/swagger-ui/index.html。

聚合后的接口文档页面如图 9-3 所示。

image 2025 04 28 13 55 10 520
Figure 1. 图9-3 聚合后的接口文档页面

从图 9-3 中可以看出,Swagger UI 页面在加载时,对 /swagger-resources 请求得到的不再是一条数据,而是上述代码中根据 5 条路由信息整合后的 5 条 Swagger 资源,与预期的效果一致,编码完成。

网关层聚合 Swagger 资源虽然不是一个特别复杂的知识点,却是企业开发中或真实的项目开发中不可缺少的一个步骤。在本章中,笔者介绍了聚合的原因、实现思路及具体的实现原理,目的是让读者更了解整个流程,而不是给几行代码让读者知道是怎么做的,却不知道为什么要这样编码。当然,本章还根据实战项目做了一些编码调整,并没有直接读取所有的路由配置来组装 Swagger 资源,毕竟不同的项目有不同的实现方式。

本章主要是在微服务架构项目中加入 “网关层聚合 Swagger 资源” 相关编码,对实战部分的讲解做补充和优化。希望读者能够根据笔者提供的开发步骤顺利地完成本章的项目改造。