1 說明
? ? ? ? ?SpringCloud項目中,微服務(wù)模塊和網(wǎng)關(guān)模塊必不可少。按照以前SpringBoot的模式,單個服務(wù)擁有自己的Api文檔(Swagger文檔),引入微服務(wù)后,多文檔管理成了一個問題。我們需要一個統(tǒng)一的入口方便前端同學(xué)查看。本篇文章就是把各個微服務(wù)的swagger-api文檔,集成到網(wǎng)關(guān)服務(wù)下面。
????????關(guān)于swagger3介紹,可見文章:?https://mp.csdn.net/mp_blog/creation/editor/127736281https://mp.csdn.net/mp_blog/creation/editor/127736281????????關(guān)于SpringCloudGateway介紹,可見文章:
Spring Cloud Gateway 服務(wù)網(wǎng)關(guān)的部署與使用詳細(xì)介紹_張維鵬的博客-CSDN博客網(wǎng)關(guān)作為系統(tǒng)的唯一流量入口,封裝內(nèi)部系統(tǒng)的架構(gòu),所有請求都先經(jīng)過網(wǎng)關(guān),由網(wǎng)關(guān)將請求路由到合適的微服務(wù),所以,使用網(wǎng)關(guān)的好處在于:(1)簡化客戶端的工作。網(wǎng)關(guān)將微服務(wù)封裝起來后,客戶端只需同網(wǎng)關(guān)交互,而不必調(diào)用各個不同服務(wù);(2)降低函數(shù)間的耦合度。 一旦服務(wù)接口修改,只需修改網(wǎng)關(guān)的路由策略,不必修改每個調(diào)用該函數(shù)的客戶端,從而減少了程序間的耦合性(3)解放開發(fā)人員把精力專注于業(yè)務(wù)邏輯的實現(xiàn)。由網(wǎng)關(guān)統(tǒng)一實現(xiàn)服務(wù)路由(灰度與ABTest)、負(fù)載均衡、訪問控制、流控熔斷降級等非業(yè)務(wù)相關(guān)功能https://blog.csdn.net/a745233700/article/details/122917167
2 代碼部分
2.1 微服務(wù)部分
? ? ? ? 假設(shè)我們已經(jīng)有3個微服務(wù)了,對應(yīng)3個api文檔,分別是:
Swagger文檔地址:
服務(wù)1:http://localhost:9001/java/swagger-ui/index.html
服務(wù)2:http://localhost:9100/files/swagger-ui/index.html
服務(wù)3:http://localhost:9200/pays/swagger-ui/index.html
對應(yīng)的API接口如下:
服務(wù)1:http://localhost:9001/java/v3/api-docs?group=YX
服務(wù)2:http://localhost:9100/files/v3/api-docs?group=YX
服務(wù)3:http://localhost:9200/pays/v3/api-docs?group=YX
2.2 網(wǎng)關(guān)部分
? ? ? ? pom.xml:引入網(wǎng)關(guān)和swagger3
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>ssm-mult-module-demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>org.example</groupId>
<artifactId>module-cloud-gateway</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>module-cloud-gateway Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- sentinel 限流+控制臺 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<!--特別注意:在 gateway 網(wǎng)關(guān)服務(wù)中不能引入 spring-boot-starter-web 的依賴,否則會報錯-->
<!-- Spring cloud gateway 網(wǎng)關(guān)依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--swagger3 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger3.version}</version>
</dependency>
</dependencies>
<!--<build>
<finalName>module-cloud-gateway</finalName>
<pluginManagement><!– lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) –>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!– see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging –>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>-->
<build>
<finalName>module-cloud-gateway</finalName>
<plugins>
<plugin>
<!--該插件主要用途:構(gòu)建可執(zhí)行的JAR -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
? ? ? ? ? application.yml:配置網(wǎng)關(guān)和轉(zhuǎn)發(fā)路由
server:
port: 8080
spring:
application:
name: module-cloud-gateway-win
cloud:
gateway: #網(wǎng)關(guān)路由配置
httpclient:
pool:
max-idle-time: 10000
routes:
# 請求:http://localhost:8080/java/remote/user/test1 會轉(zhuǎn)發(fā)到 http://localhost:9001/java/remote/user/test1
- id: java-service # 路由 id,沒有固定規(guī)則,但唯一,建議與服務(wù)名對應(yīng)
uri: http://localhost:9001 # 匹配后提供服務(wù)的路由地址
# 以下是斷言條件,必選全部符合條件
predicates:
- Path=/java/** #斷言,路徑匹配 注意:Path 中 P 為大寫
- id: files-service
uri: http://localhost:9100/
predicates:
- Path=/files/**
- id: pays-service
uri: http://localhost:9200/
predicates:
- Path=/pays/**
springfox:
documentation:
swagger-ui:
enabled: true
Swagger3Config.java:swagger配置
package com.module.nacos.file.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.*;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.*;
/***
* @author
* @date
* @apiNote 訪問鏈接:http://localhost:8080/swagger-ui/index.html#/
*/
@EnableOpenApi
@Configuration
public class Swagger3Config {
@Value("${spring.application.name}")
private String PROJECT_NAME;
/**
* 配置基本信息
* @return
*/
@Bean
public ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(PROJECT_NAME)
.description("swagger test app restful api")
.termsOfServiceUrl("http://localhost")
.contact(new Contact("SSM", "http://localhost", "xxxx@gmail.com"))
.version("1.0")
.build();
}
/**
* 配置文檔生成最佳實踐
*
* @param apiInfo
* @return
*/
@Bean
public Docket createRestApi(ApiInfo apiInfo) {
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo)
.groupName("YX")
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.paths(PathSelectors.any())
.build()
//添加token的參數(shù)
.securityContexts(securityContexts())
.securitySchemes(securitySchemes());
}
/* ↓↓↓↓ 解決swagger3.0 head傳參失效的問題 ↓↓↓↓ */
private List<SecurityScheme> securitySchemes() {
List<SecurityScheme> securitySchemes = new ArrayList<>();
securitySchemes.add(new ApiKey("Authorization", "Authorization", "header"));
securitySchemes.add(new ApiKey("Accept-Language", "Accept-Language", "header"));
return securitySchemes;
}
private List<SecurityContext> securityContexts() {
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$")).build());
return securityContexts;
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope1 = new AuthorizationScope("global", "token");
AuthorizationScope[] authorizationScopes1 = new AuthorizationScope[1];
authorizationScopes1[0] = authorizationScope1;
AuthorizationScope authorizationScope2 = new AuthorizationScope("global", "language");
AuthorizationScope[] authorizationScopes2 = new AuthorizationScope[1];
authorizationScopes2[0] = authorizationScope2;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes1));
securityReferences.add(new SecurityReference("Accept-Language", authorizationScopes2));
return securityReferences;
}
/* ↑↑↑↑ 解決swagger3.0 head傳參失效的問題 ↑↑↑↑ */
@SafeVarargs
private final <T> Set<T> hashSet(T... ts) {
if (ts.length > 0) {
return new LinkedHashSet<>(Arrays.asList(ts));
}
return null;
}
/**
* 增加如下配置可解決Spring Boot 6.x 與Swagger 3.0.0 不兼容問題
**/
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
String basePath = webEndpointProperties.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}
private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
}
SwaggerProvider.java:關(guān)鍵代碼,注意API_URI參數(shù)
package module.cloud.gateway.config.swagger;
import lombok.AllArgsConstructor;
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;
/**
* @author ssm
* @version V1.0.4
* @description TODO
* @date 2023/2/27 17:55
*/
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v3/api-docs?group=YX";
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
/**
* 這個類是核心,這個類封裝的是SwaggerResource,即在swagger-ui.html頁面中頂部的選擇框,選擇服務(wù)的swagger頁面內(nèi)容。
* RouteLocator:獲取spring cloud gateway中注冊的路由
* RouteDefinitionLocator:獲取spring cloud gateway路由的詳細(xì)信息
* RestTemplate:獲取各個配置有swagger的服務(wù)的swagger-resources
*/
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
//取出gateway的route
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
//結(jié)合配置的route-路徑(Path),和route過濾,只獲取有效的route節(jié)點(diǎn)
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_URI)))));
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("3.0.0");
return swaggerResource;
}
}
3 驗證
? ? ? ? 打開網(wǎng)關(guān)服務(wù)的Swagger鏈接:http://localhost:8080/swagger-ui/index.html
? ? ? ? 右上角可切換不同服務(wù)
4 參考鏈接
(1)?Spring Cloud Gateway 服務(wù)網(wǎng)關(guān)的部署與使用詳細(xì)介紹_張維鵬的博客-CSDN博客文章來源:http://www.zghlxwxcb.cn/news/detail-705990.html
(2)?SpringCloudAlibaba篇(八)SpringCloudGateWay聚合swagger3、SpringBoot2.6.X整合swagger3+knife4j_springboot 2.6整合springcloud gateway_fate急速出擊的博客-CSDN博客文章來源地址http://www.zghlxwxcb.cn/news/detail-705990.html
到了這里,關(guān)于SpringCloudGateway整合swagger3文檔的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!