在微服务架构下,通常每个微服务 都会使用 Swagger 来管理我们的接口文档,当微服务越来越多,接口查找管理无形中要浪费我们不少时间,因此,我们需要把其它系统的 Swagger 文档聚合到 Gateway ,方便我们统一查看接口文档。

1. 核心实现

1.1 OMS 端实现

1.1.1 swagger配置文件

@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {
/**
* 是否开启swagger
*/
private Boolean enabled;
/**
* swagger会解析的包路径
**/
private String basePackage = "";
/**
* swagger会解析的url规则
**/
private List<String> basePath = new ArrayList<>();
/**
* 在basePath基础上需要排除的url规则
**/
private List<String> excludePath = new ArrayList<>();
/**
* 标题
**/
private String title = "";
/**
* 描述
**/
private String description = "";
/**
* 版本
**/
private String version = "";
/**
* 许可证
**/
private String license = "";
/**
* 许可证URL
**/
private String licenseUrl = "";
/**
* 服务条款URL
**/
private String termsOfServiceUrl = ""; /**
* host信息
**/
private String host = "";
/**
* 联系人信息
*/
private Contact contact = new Contact();
/**
* 全局统一鉴权配置
**/
private Authorization authorization = new Authorization(); @Data
@NoArgsConstructor
public static class Contact { /**
* 联系人
**/
private String name = "";
/**
* 联系人url
**/
private String url = "";
/**
* 联系人email
**/
private String email = ""; } @Data
@NoArgsConstructor
public static class Authorization { /**
* 鉴权策略ID,需要和SecurityReferences ID保持一致
*/
private String name = ""; /**
* 需要开启鉴权URL的正则
*/
private String authRegex = "^.*$"; /**
* 鉴权作用域列表
*/
private List<AuthorizationScope> authorizationScopeList = new ArrayList<>(); private List<String> tokenUrlList = new ArrayList<>();
} @Data
@NoArgsConstructor
public static class AuthorizationScope { /**
* 作用域名称
*/
private String scope = ""; /**
* 作用域描述
*/
private String description = ""; }
}

1.1.2 swagger 自动配置类

@Configuration
@EnableSwagger2
@EnableAutoConfiguration
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerAutoConfiguration { /**
* 默认的排除路径,排除Spring Boot默认的错误处理路径和端点
*/
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error","/actuator/**");
private static final String BASE_PATH = "/**"; @Bean
@ConditionalOnMissingBean
public SwaggerProperties swaggerProperties() {
return new SwaggerProperties();
} @Bean
public Docket api(SwaggerProperties swaggerProperties) {
// base-path处理
if (swaggerProperties.getBasePath().isEmpty()) {
swaggerProperties.getBasePath().add(BASE_PATH);
}
//noinspection unchecked
List<Predicate<String>> basePath = new ArrayList();
swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path))); // exclude-path处理
if (swaggerProperties.getExcludePath().isEmpty()) {
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
}
List<Predicate<String>> excludePath = new ArrayList<>();
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path))); //noinspection Guava
return new Docket(DocumentationType.SWAGGER_2)
.host(swaggerProperties.getHost())
.apiInfo(apiInfo(swaggerProperties)).select()
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
.paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
.build()
.securitySchemes(Collections.singletonList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext()))
.pathMapping("/");
} /**
* 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL
*
* @return
*/
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex()))
.build();
} /**
* 默认的全局鉴权策略
*
* @return
*/
private List<SecurityReference> defaultAuth() {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
return Collections.singletonList(SecurityReference.builder()
.reference(swaggerProperties().getAuthorization().getName())
.scopes(authorizationScopeList.toArray(authorizationScopes))
.build());
} private OAuth securitySchema() {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
ArrayList<GrantType> grantTypes = new ArrayList<>();
swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes);
} private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.license(swaggerProperties.getLicense())
.licenseUrl(swaggerProperties.getLicenseUrl())
.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
.version(swaggerProperties.getVersion())
.build();
} }

1.1.3 设置允许跨域

如果不设置允许跨域,则 Gateway 无法跨域调用 OMS 的 Swagger 文档,因此,需要设置 swagger 接口允许跨域。

package com.ftl.oms.config;

@Configuration
public class GlobalCorsConfig { @Bean
public CorsFilter corsFilter() {
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否发送Cookie信息
config.setAllowCredentials(true);
//放行哪些原始域(请求方式)
config.addAllowedMethod("*");
//放行哪些原始域(头部信息)
config.addAllowedHeader("*");
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
config.addExposedHeader("Access-Control-Allow-*"); //2.添加映射路径
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/api/**", config);
configSource.registerCorsConfiguration("/v2/api-docs", config); //3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}

1.2 GATEWAY 端实现

1.2.1 swagger配置文件


@Data
@Component
@ConfigurationProperties("swagger")
public class SwaggerProperties { /**
* 资源名称
*/
private List<Resource> resources; /**
* 是否开启swagger
*/
private Boolean enabled;
/**
* swagger会解析的包路径
**/
private String basePackage = "";
/**
* swagger会解析的url规则
**/
private List<String> basePath = new ArrayList<>();
/**
* 在basePath基础上需要排除的url规则
**/
private List<String> excludePath = new ArrayList<>();
/**
* 标题
**/
private String title = "";
/**
* 描述
**/
private String description = "";
/**
* 版本
**/
private String version = "";
/**
* 许可证
**/
private String license = "";
/**
* 许可证URL
**/
private String licenseUrl = "";
/**
* 服务条款URL
**/
private String termsOfServiceUrl = ""; /**
* host信息
**/
private String host = "";
/**
* 联系人信息
*/
private Contact contact = new Contact();
/**
* 全局统一鉴权配置
**/
private Authorization authorization = new Authorization(); @Data
@NoArgsConstructor
public static class Contact { /**
* 联系人
**/
private String name = "";
/**
* 联系人url
**/
private String url = "";
/**
* 联系人email
**/
private String email = ""; } @Data
@NoArgsConstructor
public static class Authorization { /**
* 鉴权策略ID,需要和SecurityReferences ID保持一致
*/
private String name = ""; /**
* 需要开启鉴权URL的正则
*/
private String authRegex = "^.*$"; /**
* 鉴权作用域列表
*/
private List<AuthorizationScope> authorizationScopeList = new ArrayList<>(); private List<String> tokenUrlList = new ArrayList<>();
} @Data
@NoArgsConstructor
public static class AuthorizationScope { /**
* 作用域名称
*/
private String scope = ""; /**
* 作用域描述
*/
private String description = ""; } @Data
@NoArgsConstructor
public static class Resource { /**
* 资源名称
*/
private String name; /**
* 转发路径
*/
private String url; /**
* 文档版本
*/
private String swaggerVersion;
}
}

注:GATEWAY 端比 OMS 端的配置文件多出了 private List<Resource> resources; ,这个主要功能是从配置文件中得到 OMS 的 swagger 接口文档接口, 从而聚合 其它服务的文档。

1.2.2 swagger 自动配置类

 

@Configuration
@EnableSwagger2
@EnableAutoConfiguration
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerAutoConfiguration { /**
* 默认的排除路径,排除Spring Boot默认的错误处理路径和端点
*/
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error","/actuator/**");
private static final String BASE_PATH = "/**"; @Bean
@ConditionalOnMissingBean
public SwaggerProperties swaggerProperties() {
return new SwaggerProperties();
} @Bean
public Docket api(SwaggerProperties swaggerProperties) {
// base-path处理
if (swaggerProperties.getBasePath().isEmpty()) {
swaggerProperties.getBasePath().add(BASE_PATH);
}
//noinspection unchecked
List<Predicate<String>> basePath = new ArrayList();
swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path))); // exclude-path处理
if (swaggerProperties.getExcludePath().isEmpty()) {
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
}
List<Predicate<String>> excludePath = new ArrayList<>();
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path))); //noinspection Guava
return new Docket(DocumentationType.SWAGGER_2)
.host(swaggerProperties.getHost())
.apiInfo(apiInfo(swaggerProperties)).select()
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
.paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
.build()
.securitySchemes(Collections.singletonList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext()))
.pathMapping("/");
} /**
* 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL
*
* @return
*/
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex()))
.build();
} /**
* 默认的全局鉴权策略
*
* @return
*/
private List<SecurityReference> defaultAuth() {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
return Collections.singletonList(SecurityReference.builder()
.reference(swaggerProperties().getAuthorization().getName())
.scopes(authorizationScopeList.toArray(authorizationScopes))
.build());
} private OAuth securitySchema() {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
ArrayList<GrantType> grantTypes = new ArrayList<>();
swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes);
} private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.license(swaggerProperties.getLicense())
.licenseUrl(swaggerProperties.getLicenseUrl())
.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
.version(swaggerProperties.getVersion())
.build();
} }

1.2.3 swagger 资源配置信息

这个类主要是用来暴露 swagger 相关资源的接口,允许浏览器加载所对应的资源文件


@RestController
@RequestMapping("/swagger-resources")
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("/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
} @GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
} @GetMapping("")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}

1.2.4 swagger 聚合配置

这个类主要负责的功能是将其它系统的文档聚合到 gateway,重写 SwaggerResourcesProvider#get() 方法,从 yml 配置文件中,得到其它系统的 swagger 文档信息,然后gateway 转发到不同的系统的 swagger。


@Component
@Primary
@AllArgsConstructor
public class SwaggerProviderConfiguration implements SwaggerResourcesProvider { private static final String API_URI = "/v2/api-docs"; @Autowired
private SwaggerProperties swaggerProperties; @Override
public List<SwaggerResource> get() {
List<SwaggerProperties.Resource> resourcesPropertes = swaggerProperties.getResources();
if (resourcesPropertes.isEmpty()) {
return Lists.newArrayList();
} // 从yml配置文件中加载其它系统的swagger资源
List<SwaggerResource> resources = new ArrayList<>(); resourcesPropertes.forEach(resource -> {
resources.add(this.swaggerResource(resource.getName(), resource.getUrl() + API_URI, resource.getSwaggerVersion()));
}); return resources;
} private SwaggerResource swaggerResource(String name, String url, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(url);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}

2. 展示效果

聚合其它系统 swagger 文档展示

3. 参考文档

springboot+swagger接口文档企业实践(上)

SpringCloud Gateway聚合各个服务的Swagger

Spring Cloud Gateway 聚合swagger文档


swagger 编写规范

Gateway 聚合swagger文档的更多相关文章

  1. 微服务如何聚合 API 文档?这波秀~

    今天这篇文章介绍一下微服务如何聚合Swagger实现接口文档管理. 文章目录如下: 为什么需要聚合? 微服务模块众多,如果不聚合文档,则访问每个服务的API文档都需要单独访问一个Swagger UI界 ...

  2. 使用 Swagger 文档化和定义 RESTful API

    大部分 Web 应用程序都支持 RESTful API,但不同于 SOAP API——REST API 依赖于 HTTP 方法,缺少与 Web 服务描述语言(Web Services Descript ...

  3. 白话SpringCloud | 第十一章:路由网关(Zuul):利用swagger2聚合API文档

    前言 通过之前的两篇文章,可以简单的搭建一个路由网关了.而我们知道,现在都奉行前后端分离开发,前后端开发的沟通成本就增加了,所以一般上我们都是通过swagger进行api文档生成的.现在由于使用了统一 ...

  4. flask - fastapi (python 异步API 框架 可以自动生成swagger 文档) 常用示例 以及整合euraka nacos

    flask - fastapi    (python 异步API 框架  可以自动生成swagger 文档)  常用示例: 之前使用 flask 需要手动写文档, 这个可以自动生成, fastapi ...

  5. revel + swagger 文档也能互动啦

    beego 从 1.3 后开始支持自动化API文档,不过,目测比较复杂,一直期望 revel 能有官方支持. revel 确实已经有了官方支持的计划,有可能将在 0.14 版本支持,现在才 0.11. ...

  6. Swagger文档转Word 文档

    GitHub 地址:https://github.com/JMCuixy/SwaggerToWord/tree/developer 原创作品,转载请注明出处:http://www.cnblogs.co ...

  7. 利用typescript生成Swagger文档

    项目地址:https://github.com/wz2cool/swagger-ts-doc demo代码地址:https://github.com/wz2cool/swagger-ts-doc-de ...

  8. springboot成神之——swagger文档自动生成工具

    本文讲解如何在spring-boot中使用swagger文档自动生成工具 目录结构 说明 依赖 SwaggerConfig 开启api界面 JSR 303注释信息 Swagger核心注释 User T ...

  9. asp.net core 2.1 生成swagger文档

    新建asp.netcore2.1 api项目 “WebApplication1” 在nuget管理器中添加对Swashbuckle.AspNetCore 3.0.0.Microsoft.AspNetC ...

  10. Swagger文档转Word

    Swagger文档转Word 文档   GitHub 地址:https://github.com/JMCuixy/SwaggerToWord/tree/developer 原创作品,转载请注明出处:h ...

随机推荐

  1. Flutter制作桌面图标长按展示操作项面板

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...

  2. 浅析NodeJS中的事件循环和异步API

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...

  3. TVM:Schedule的理解

    schedule与计算逻辑分离是自动代码生成技术的核心概念,由MIT CASIL组的Jonathan Ragan-Kelley在2012年发表在SIGGRAPH上的文章率先提出,然后在2013年发表在 ...

  4. Web前端入门第 57 问:JavaScript 数据类型与类型转换

    在程序语言中,数据类型是基础,一切程序都是建立在基础数据之上. 如果说程序如同万丈高楼平地起,那么数据类型就像沙.石.钢筋.水泥等等最基础的原料.一样的高楼,不同的人,用相同的原料,造的方法也会有千般 ...

  5. HashMap的数据结构和源码分析

    如果想透彻理解什么是HashMap,首先需要知道HashMap的数据结构是什么:其次需要厘清它能做什么,即它的功能:最后,还需要知道HashMap怎么实现这些功能的.下面我们针对这三个方面展开剖析. ...

  6. RabbitMQ单机&可能遇到的问题

    1.Deployments 1 kind: Deployment 2 apiVersion: apps/v1 3 metadata: 4 name: rabbitmq-deployment 5 spe ...

  7. 启智树提高组day4T1 T1(t1.cpp,1s,512MB)

    启智树提高组day4T1 T1(t1.cpp,1s,512MB) 题面描述 对⼀个⻓度为2n 的实数序列A考虑下列问题: 设S为序列中所有元素的和.你可以做下列操作n次: 选择两个未被选中过的下标i和 ...

  8. 二叉排序树BST及CRUD操作

    摘要 构造一颗二叉排序树(也叫二叉搜索树,BST,Binary Search Tree)十分简单.一般来讲,大于根节点的放在根节点的右子树上,小于根节点的放在根节点的左子树上(如果等于根节点,则可视情 ...

  9. 「Log」2023.8.17 小记

    序幕 早上到校先摆,然后开调代码. 大分块对拍调调调. 学长开始讲平衡树. 平衡树平衡树平衡树! 学完了,点午饭吃午饭. 学主席树. 主席树主席树主席树! 学完了点晚饭吃完饭. 用 chatGPT 写 ...

  10. Springboot笔记<4>@Autowired和@Resource的区别

    @Autowired和@Resource的区别 @Resource 有两个常用属性name.type,所以分4种情况 指定name和type:通过name找到唯一的bean,找不到抛出异常:如果typ ...