通过自定义### 问题描述
网关集成swagger 通过网关访问swagger在线接口文档,配置不生效
问题出现的环境背景及自己尝试过哪些方法
1、上网查询过swagger集成资料
2、借鉴网上开源项目集成swagger
3、业务模块实体类、属性、Controller层入口均已加上swagger相关注解
相关代码
// 请把代码文本粘贴到下方(请勿用图片代替代码)
自定义starter Swagger 将通用配置提取到starter中、避免每个模块都重写一遍swagger配置代码如下:
Swagger 配置类
package com.bus.cloud.swagger.config;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author seata
* swagger配置 作为公用starter
*/
@Configuration
@EnableSwagger2
@EnableConfigurationProperties(SwaggerProperties.class)
@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()
.pathMapping("/");
}
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();
}
}
==================SwaggerProperties 属性=======================
package com.bus.cloud.swagger.config;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* SwaggerProperties
*
* @Author: seata
* @CreateDate: 2021/9/3 10:57
* @UpdateUser: seata
* @UpdateDate: 2021/9/3 10:57
* @UpdateRemark: 修改内容
* @Version: 1.0
*/
@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 = "";
}
}
==============自定义swagger注解=========
``` java
package com.bus.cloud.swagger.annotation;
import com.bus.cloud.swagger.config.SwaggerAutoConfiguration;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* 开启 swagger
* @Author: cong zhi
* @CreateDate: 2021/9/3 10:54
* @UpdateUser: cong zhi
* @UpdateDate: 2021/9/3 10:54
* @UpdateRemark: 修改内容
* @Version: 1.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({SwaggerAutoConfiguration.class})
public @interface EnableSwagger {
}
复制代码
============META-INF文件夹,spring.factories文件配置============
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.bus.cloud.swagger.config.SwaggerAutoConfiguration
### bus-starter-swagger工程 pom 文件依赖
```pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bus.cloud</groupId>
<artifactId>bus-core</artifactId>
<version>0.0.3-SNAPSHOT</version>
</parent>
<groupId>com.bus.cloud</groupId>
<artifactId>bus-starter-swagger</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>${project.artifactId}</description>
<dependencies>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<showDeprecation>true</showDeprecation>
<compilerArgument>-Xlint:unchecked</compilerArgument>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
复制代码
GateWay 网关聚合Swagger
===== GateWay pom文件依赖=========
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bus.cloud</groupId>
<artifactId>bus-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.bus.cloud</groupId>
<artifactId>bus-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>station-gateway</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.bus.cloud</groupId>
<artifactId>bus-starter-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>bus-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>com.bus.cloud</groupId>
<artifactId>bus-starter-swagger</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- SpringCloud gateway 网关治理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.bus.cloud.code.BusGeneratorCodeApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
复制代码
聚合接口文档注册,注入路由到SwaggerResource,在网关服务中增加转发处理逻辑
package com.bus.cloud.gateway.config;
import com.google.common.collect.Lists;
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.List;
/**
* GatewaySwaggerResourcesProvider
* @Author: seata
* @CreateDate: 2021/9/4 9:11
* @UpdateUser: seata
* @UpdateDate: 2021/9/4 9:11
* @UpdateRemark: 修改内容
* @Version: 1.0
*/
@Component
@Primary
public class SwaggerResourceProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
public SwaggerResourceProvider(RouteLocator routeLocator,GatewayProperties gatewayProperties) {
this.routeLocator = routeLocator;
this.gatewayProperties=gatewayProperties;
}
/**
* 当请求文档服务时会调用这个接口,转发逻辑和定义的文档名称
* @return
*/
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = Lists.newArrayList();
List<String> routes = Lists.newArrayList();
//取出gateway的route
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
//结合配置的route-路径(Path),和route过滤,只获取有效的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_URI)))));
return resources;
}
/**
* 获取到配置路由信息,重写swaggerResource
* @param name
* @param location
* @return
*/
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
复制代码
网关上请求头过滤器
package com.bus.cloud.gateway.handler;
import com.bus.cloud.gateway.config.SwaggerResourceProvider;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
/**
* 网关上请求头过滤
* @Author: seata
* @CreateDate: 2021/9/4 17:54
* @UpdateUser: seata
* @UpdateDate: 2021/9/4 17:54
* @UpdateRemark: 修改内容
* @Version: 1.0
*/
@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
/**
* swagger 在拼装URL 数据时候,会增加X-Forwarder-Prefix 请求头里面的信息为前缀
*/
private static final String HEADER_NAME = "X-Forwarded-Prefix";
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (!StringUtils.endsWithIgnoreCase(path, SwaggerResourceProvider.API_URI)) {
return chain.filter(exchange);
}
String basePath = path.substring(0, path.lastIndexOf(SwaggerResourceProvider.API_URI));
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
};
}
}
复制代码
资源的提供handler
package com.bus.cloud.gateway.handler;
import com.bus.cloud.gateway.config.SwaggerResourceProvider;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import java.util.Optional;
/**
*
- 因为Gateway里没有配置SwaggerConfig,而运行Swagger-ui又需要依赖一些接口 ,同时使用gateway后(用到了webFlux排除掉了web),不能引入@EnableSwagger等注解,故自定义swagger的接口
- 因为Swagger暂不支持webflux项目,所以Gateway里不能配置SwaggerConfig
- SwaggerHandler
- @Author: seata
- @CreateDate: 2021/9/4 9:30
- @UpdateUser: seata
- @UpdateDate: 2021/9/4 9:30
- @UpdateRemark: 修改内容
- @Version: 1.0
*/
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerResourceProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerResourceProvider 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)));
}
复制代码
}
GateWay 网关application.yml配置
# 应用名称
#spring.application.name=station-gateway
###服务启动端口号
server:
port: 8040
# ssl:
# key-password: sdadffff
# key-store: classpath:wx.sss.com.jks
# key-store-type: JKS
# 服务名称
spring:
application:
name: @artifactId@
instance_id: @artifactId@
# 开启 Gateway 服务注册中心服务发现
cloud:
gateway:
discovery:
locator:
enabled: true
lowerCaseServiceId: true
routes:
# 路由的ID
- id: bus-org
uri: lb://bus-org # uri 代表路由的目标地址。注意:uri地址后面不要加 " / "
predicates:
- Path=/bus-org/**
- id: station-auth
uri: lb://bus-auth
predicates:
- Path=/bus-auth/**
- id: bus-estate-service
uri: lb://bus-estate-service
predicates:
- Path=/bus-estate-service/**
- id: bus-web
uri: lb://bus-web
predicates:
- Path=/bus-web/**
filters:
- StripPrefix=1
- SwaggerHeaderFilter #swagger过滤器
mvc:
static-path-pattern: /PC/**
resources:
static-locations:
- file:${web.view}
main:
allow-bean-definition-overriding: true
security:
user:
name: admin
password: admin
# redis 相关配置
redis:
database: 1
port: 6379
host: 192.168.110.105 # Redis服务器地址
# password: huayue&123
timeout: 5000ms #连接超时时间(毫秒)
lettuce:
pool:
max-active: 8 #连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms #连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 8 #连接池中的最大空闲连接
min-idle: 2 #连接池中最小连接数
# Eureka Server 配置
eureka:
instance:
non-secure-port: ${server.port:1111}
prefer-ip-address: true # 指定ip 地址,默认为false
instanceId: ${spring.application.name}:${spring.application.instance_id:${server.port}} # 指定应用名称和应用端口
client:
service-url:
defaultZone: http://localhost:${eureka.port:8761}/eureka/
# 配置Gateway日志等级,输出转发细节信息
logging:
level:
org.springframework.cloud.gateway: warn
secure:
ignored:
urls: #配置白名单路径
- "/actuator/**"
- "/bus-auth/auth/login"
#auth:
# url: http://api-auth/SysUser/tokenToId?token=
web:
view: E:/Source/Branch/web_project/
复制代码
业务模块启动类
package com.bus.cloud;
import com.bus.cloud.swagger.annotation.EnableSwagger;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
/**
* 巴士物业
* @Author: seata
* @CreateDate: 2021/8/14 22:56
* @UpdateUser: seata
* @UpdateDate: 2021/8/14 22:56
* @UpdateRemark: 修改内容
* @Version: 1.0
*/
@EnableSwagger
//@EnableFeignClients
@SpringCloudApplication
@MapperScan("com.bus.*.mapper")
public class BusEstateServiceApplication {
public static void main(String[] args) {
SpringApplication.run(BusEstateServiceApplication.class, args);
}
}
复制代码
业务模块 application.yml 配置文件
# 切换配置文件
spring:
profiles:
active: test
swagger:
enabled: true
base-package: com.bus.cloud.controller
version: 1.0.0
title: 物业管理
description: API文档自动生成
复制代码
你期待的结果是什么?实际看到的错误信息又是什么?
启动成功期望正常访问swagger在线接口文档,实际无任何输出,没有任何报错




近期评论