Spring Cloud Gateway 整合 Springfox 3.0.0 实现OpenAPI 3 文档管理


一、前言

OpenAPI 简介

OpenAPI规范是一个由社区驱动的开放规范,这个规范定义了一个标准的、语言无关的 RESTful API 接口规范,它可以同时允许开发人员和操作系统查看并理解某个服务的功能,而无需访问源代码,文档或网络流量检查。合理地定义OpenAPI规范,能使开发者更好的理解远程服务和与之交互。目前最新的规范是OpenAPI Specification 3.0.3。

Swagger简介

Swagger是一个规范和完整的框架,用于生成 、描述、调用和可视化 RESTful 风格的 Web 服务,简化了开发者开发API。

Springfox简介

SpringFox是 Spring 社区非官方维护的一个项目,主要就是帮助使用者将Swagger集成到Spring 中,在停更了2年多之后,近期终于推出了3.0.0版本,支持了开发者期盼已久的OpenApi 3规范。

二、Springfox 3.0.0 在Spring Boot 项目下的用法

导入依赖(Gradle)

implementation('io.springfox:springfox-boot-starter:3.0.0')

开启注解

@EnableOpenApi

编写配置类

/**
 * @author Vieruodis
 * @description Springfox openApi 3.0 的配置类
 * @date 2020/7/15  21:43
 */
@Configuration
@EnableOpenApi
public class SpringfoxOpenApiConfig {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30)
            .pathMapping("/")
                // 定义是否开启swagger,false为关闭,可以通过变量控制
                .enable(true)
                // 将api的元信息设置为包含在json ResourceListing响应中。
                .apiInfo(apiInfo())
                // 选择哪些接口作为swagger的doc发布
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.vieruodis.vmall.manage.product.controller"))
                .paths(PathSelectors.any())
                .build()
                .groupName("defaultBB")
                // 支持的通讯协议集合
                .protocols(Set.of("https", "http"))
                ;
    }

    /**
     * API 页面上半部分展示信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .version("1.0.0")
                .title("Vieruodis Api Doc")
                .description("Vmall shop application")
                .contact(new Contact("Vieruodis", null, "huaiyuan0660@outlook.com"))
                .termsOfServiceUrl("http://localhost:88/")
                .license("Apache 2.0")
                .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0.html")
                .build();
    }
}

编写Controller的注解

@Api(value = "AlbumController")
public interface AlbumControllerApi {

    /**
     * Album 多条件分页查询
     *
     * @param page  页码
     * @param size  每页数
     * @param album 查询条件
     * @return 封装了 【Album 分页数据】 的响应结果
     */
    @ApiOperation(value = "Album条件分页查询",notes = "分页条件查询Album方法详情",tags = {"AlbumController"})
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "page", value = "当前页", required = true, dataType = "Integer"),
            @ApiImplicitParam(paramType = "path", name = "size", value = "每页显示条数", required = true, dataType = "Integer")
    })
    PageResponseResult<Album> findPage(int page, int size, @ApiParam(name = "Album对象",value = "传入JSON数据") Album album);

    /**
     * Album 分页查询
     *
     * @param page 页码
     * @param size 每页数
     * @return 封装了 【Album 分页数据】 的响应结果
     */
    @ApiOperation(value = "Album分页查询",notes = "分页查询Album方法详情",tags = {"AlbumController"})
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "page", value = "当前页", required = true, dataType = "Integer"),
            @ApiImplicitParam(paramType = "path", name = "size", value = "每页显示条数", required = true, dataType = "Integer")
    })
    PageResponseResult<Album> findPage(int page, int size);

    /**
     * Album 多条件查询
     *
     * @param album 查询条件
     * @return 封装了 【Album 数据集】 的响应结果
     */

    @ApiOperation(value = "Album条件查询",notes = "条件查询Album方法详情",tags = {"AlbumController"})
    CommonResponseResult<List<Album>> findList(@ApiParam(name = "Album对象",value = "传入JSON数据") Album album);

    /**
     * 查询所有 Album
     *
     * @return 封装了 【Album 数据集】 的响应结果
     */
    @ApiOperation(value = "查询所有Album",notes = "查询所Album有方法详情",tags = {"AlbumController"})
    CommonResponseResult<List<Album>> findAll();

    /**
     * 根据ID查询 Album
     *
     * @param id 实体ID
     * @return 封装了 【Album 数据】 的响应结果
     */
    @ApiOperation(value = "Album根据ID查询",notes = "根据ID查询Album方法详情",tags = {"AlbumController"})
    @ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
    CommonResponseResult<Album> findById(Long id);

    /**
     * 新增 Album
     *
     * @param album 新增实体
     * @return 通用响应结果
     */
    @ApiOperation(value = "Album添加",notes = "添加Album方法详情",tags = {"AlbumController"})
    ResponseResult add(@ApiParam(name = "Album对象",value = "传入JSON数据",required = true) Album album);

    /**
     * 修改 Album
     *
     * @param id    实体ID
     * @param album Album 数据
     * @return 通用响应结果
     */
    @ApiOperation(value = "Album根据ID修改",notes = "根据ID修改Album方法详情",tags = {"AlbumController"})
    @ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
    ResponseResult update(Long id, @ApiParam(name = "Album对象",value = "传入JSON数据") Album album);

    /**
     * 删除 Album
     *
     * @param id 实体ID
     * @return 通用响应结果
     */
    @ApiOperation(value = "Album根据ID删除",notes = "根据ID删除Album方法详情",tags = {"AlbumController"})
    @ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
    ResponseResult delete(Long id);

}

编写实体的注解

@Data
@ApiModel(description = "Album",value = "Album")
@Entity
@Table(name="tb_album")
public class Album implements Serializable {

    /**
     * 编号
     */
    @ApiModelProperty(value = "编号")
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    /**
     * 相册名称
     */
    @ApiModelProperty(value = "相册名称")
    @Column(name = "title")
    private String title;
    /**
     * 相册封面
     */
    @ApiModelProperty(value = "相册封面")
    @Column(name = "image")
    private String image;
    /**
     * 图片列表
     */
    @ApiModelProperty(value = "图片列表")
    @Column(name = "image_items")
    private String imageItems;
}

测试

打开: http://localhost:8000/swagger-ui/index.html

三、与Spring Cloud Gateway集成

整合思路

1,定义一个SwaggerProvider类用于填充右上角下拉框的数据,格式是(name,oas3Url)

2,定义过滤器,对oas3Url进行修正,让其能够获取文档

实践

导入依赖(Gradle)

implementation('io.springfox:springfox-boot-starter:3.0.0')

定义SwaggerProvider类填充右上角下拉框数据

@ConfigurationProperties(prefix = "open-api.swagger")
@EnableConfigurationProperties
@Component
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {

    @Setter
    @Getter
    private List<String> resources;

    public static final String API_URI = "/v3/api-docs";

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> swaggerResources = new ArrayList<>();
        resources.forEach(s -> {
            //name,oas3Url,group
            String[] split = s.split(",");
            swaggerResources.add(resource(split[0], split[1] + API_URI, split[2]));
        });
        return swaggerResources;
    }

    /**
     * @param name    显示的名字
     * @param oas3Url 文档格式:/manage/product/v3/api-docs(?group=...)
     */
    private SwaggerResource resource(
            String name,
            String oas3Url,
            String swaggerGroup) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        //url: 要看其他微服务是否设置了group 设置了的话
        if (Docket.DEFAULT_GROUP_NAME.equals(swaggerGroup)) {
            swaggerResource.setUrl(oas3Url);
        } else {
            //其他微服务是否设置了group 需要加上"?group=" + 组名
            swaggerResource.setUrl(oas3Url + "?group=" + swaggerGroup);
        }
        System.out.println("name: " + name + " url: " + oas3Url);
        swaggerResource.setSwaggerVersion("3.0.3");
        return swaggerResource;
    }

}

填充的数据来源于配置文件:

open-api:
  swagger:
    # 仅供 springfox 使用
    resources: # name,oas3Url,group
      - manage-product-service-1,/manage/product,defaultBB
      - manage-product-service-2,/manage/product,manage-product
      - file-service,/file,default

上面我给它填充的url是:localhost:端口号/【manage/product】/v3/api-docs(?group= ) 形式的,目的是为了让它找到微服务,因为我的路由设置是:

spring:
  cloud:
    gateway:
      routes:
        - id: vmall-manage-product-service
          uri: lb://vmall-manage-product
          predicates:
            - Path=/manage/product/**

 但是Springfox获取文档的地址格式是:localhost:端口号/v3/api-docs(?group= ),所以我们需要定义一个过滤器,让它能够获取到文档:

@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            System.out.println("path:"+path);
            if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) {
                return chain.filter(exchange);
            }
            ServerHttpRequest newRequest = request.mutate().path(SwaggerProvider.API_URI).build();
            ServerWebExchange newWebExchange = exchange.mutate().request(newRequest).build();
            return chain.filter(newWebExchange);
        };
    }
}

给路由配上过滤器: 

spring:
  cloud:
    gateway:
      routes:
        - id: vmall-manage-product-service
          uri: lb://vmall-manage-product
          predicates:
            - Path=/manage/product/**
          filters:
            - SwaggerHeaderFilter

 测试:

打开:http://localhost:88/swagger-ui/index.html 

测试调用文档的方法,请求的是 localhost:88/manage/product/album , 这样通过网关就能找到对应的微服务,并且正确返回json数据了。

四、总结 

Springfox3.0.0中支持了webflux,所以就不需要再写swaggerHandler解决请求映射了,相比SpringDoc openApi整合Spring Cloud Gateway 比较简单,个人比较推荐用来整合Spring Cloud Gateway。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM