1.子服务整合swagger
引入依赖
<swagger-version>3.0.0</swagger-version>
<knife4j-spring-ui-version>3.0.3</knife4j-spring-ui-version>
<dependencies>
<!-- 整合swagger start -->
<!-- 3.0版本只需要引入一个,不再像2.x版本需要引入两个包 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger-version}</version>
</dependency>
<!-- ui美化依赖 -->
<!--swagger UI-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-ui</artifactId>
<version>${knife4j-spring-ui-version}</version>
</dependency>
<!-- 整合swagger end -->
</dependencies>
swagger配置文件
package com.ly.tulip.commons.swagger.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.RequestParameterBuilder;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ParameterType;
import springfox.documentation.service.RequestParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.List;
/**
* 类说明: swagger 配置类
*
* @author wqf
* @date 2022/7/26 10:23
*/
@Component
@EnableOpenApi
public class SwaggerConfig {
/**
* 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
*/
@Value("${swagger.enable:false}")
private Boolean enable;
/**
* 项目应用名
*/
@Value("${swagger.application-name}")
private String applicationName;
/**
* 项目版本信息
*/
@Value("${swagger.application-version}")
private String applicationVersion;
/**
* 项目描述信息
*/
@Value("${swagger.application-description}")
private String applicationDescription;
@Bean
public Docket docket() {
return new Docket(DocumentationType.OAS_30)
.pathMapping("/")
// 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭
.enable(enable)
//配置api文档元信息
.apiInfo(apiInfo())
// 选择哪些接口作为swagger的doc发布
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.globalRequestParameters(getGlobalRequestParameters());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(applicationName)
.description(applicationDescription)
.contact(new Contact("xxxx平台接口文档", "www.xxx.com", "xxx.com"))
.version(applicationVersion)
.build();
}
//添加全局通用参数 一般用添加请求token
private List<RequestParameter> getGlobalRequestParameters() {
List<RequestParameter> parameters = new ArrayList<>();
parameters.add(new RequestParameterBuilder()
.name("Authentication")
.description("身份令牌")
.required(true)
//添加参数到请求头中
.in(ParameterType.HEADER)
.query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
.required(false)
.build());
return parameters;
}
}
}
yml配置swagger相关信息
swagger:
enable: true
application-name: 网关服务
application-version: 1.0
application-description: 网关服务
配置完,访问测试地址: http://ip:端口号/doc.html#/home
在微服务中存在多个服务,可以将以上配置部分做成公共模块,哪个模块需要引入swagger,引入该公共模块就行了。
2.整合到gateway
1.引入依赖 ,同上
2.gateway模块添加两配置文件,如下
SwaggerHandler
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.SwaggerResourcesProvider;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import java.util.Optional;
/**
* SWAGGER-RESOURCES控制对象
* @author ROCKY
*/
@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));
}
@SuppressWarnings("rawtypes")
@GetMapping("")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
SwaggerResourcesProvider
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
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 wqf
* @date 2022/7/26 16:47
*/
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
// SWAGGER3默认的URL后缀
public static final String SWAGGER3URL = "/v3/api-docs";
// 网关路由
@Autowired
private RouteLocator routeLocator;
@Autowired
private GatewayProperties gatewayProperties;
// 聚合其他服务接口
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resourceList = new ArrayList<>();
List<String> routes = new ArrayList<>();
// 获取网关中配置的route
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
for (RouteDefinition routeDefinition : gatewayProperties.getRoutes()) {
if (routes.contains(routeDefinition.getId())) {
for (PredicateDefinition predicateDefinition : routeDefinition.getPredicates()) {
if ("Path".equalsIgnoreCase(predicateDefinition.getName())) {
resourceList.add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs()
.get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER3URL)));
}
}
}
}
return resourceList;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("3.0");
return swaggerResource;
}
}
注意点:
1. 网关对:/swagger-ui/**; /v3/api-docs 等swagger访问路径记得放行
2. 网关配置文件需要配置路由规则,以下配置是按服务名称进行路由转发的,好处是服务端口改了也没事。
server:
port: 10000
spring:
profiles:
active: dev
application:
name: gateway-server
#配置网关路由规则 按服务名进行路由
cloud:
gateway:
discovery:
locator:
# 是否与服务发现组件结合,通过serviceId 转发到具体的服务实例
enabled: true #是否开启基于服务发现的路由规则
lower-case-service-id: true # 是否将服务名称转小写
routes:
# 唯一标识 - 系统核心服务
- id: base-server
uri: lb://base-server
#断言,路径相匹配的进行路由,配置服务端的方法路
predicates:
- Path=/base-server/**
filters:
- StripPrefix=0
# 唯一标识 - 系统用户服务
- id: auth-server
uri: lb://auth-server
predicates:
- Path=/auth-server/**
filters:
- StripPrefix=0
- yml配置了几个服务的路由,则只有当所有服务都正常时,swagger访问页面才会正常,如果配置两个服务的路由规则,但只启动一个服务swagger是会报错的
出现的问题:
在swagger页面测试请求时,发现发出去的请求没有带上服务名导致访问404,如:http://127.0.0.1:10000/logout
正确请求是:http://127.0.0.1:10000/auth-server/logout
解决方法:java目录下创建目录 springfox.documentation.oas.web
在该目录下创建文件SpecGeneration
package springfox.documentation.oas.web;
import io.swagger.v3.oas.models.servers.Server;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import static org.slf4j.LoggerFactory.getLogger;
/**
* 类说明: 处理swagger 模拟请求时 没有追加服务名的问题
*
* @author wqf
* @date 2022/7/26 16:29
*/
@Slf4j
public class SpecGeneration {
private static final String HEADER_NAME = "X-Forwarded-Prefix";
private static final Logger LOGGER = getLogger(SpecGeneration.class);
public static final String OPEN_API_SPECIFICATION_PATH
= "${springfox.documentation.open-api.v3.path:/v3/api-docs}";
protected static final String HAL_MEDIA_TYPE = "application/hal+json";
private SpecGeneration() {
throw new UnsupportedOperationException();
}
/**
* 创建一个默认的 swagger 的server
*
* @param requestPrefix /v3/api-docs
* @param requestUrl 请求的url
* @return Server
*/
public static Server inferredServer(String requestPrefix, String requestUrl) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String serverUrl = requestUrl.replace(requestPrefix, "");
String header = null;
try {
URI url = new URI(requestUrl);
serverUrl = String.format("%s://%s:%s", url.getScheme(), url.getHost(), url.getPort());
header = request.getHeader(HEADER_NAME);
if (!StringUtils.isEmpty(header)) {
log.info("当前的服务为:{}", header);
serverUrl += header;
}
} catch (URISyntaxException e) {
LOGGER.error("Unable to parse request url:" + requestUrl);
}
String description = "Inferred Url";
if (!StringUtils.isEmpty(header)) {
description += " For " + header.substring(1);
}
return new Server()
.url(serverUrl)
.description(description);
}
public static String decode(String requestURI) {
try {
return URLDecoder.decode(requestURI, StandardCharsets.UTF_8.toString());
} catch (UnsupportedEncodingException e) {
return requestURI;
}
}
}
2022/07/27 补充
knife4j-spring-ui 依赖整合还是有点问题,可以先移除这个依赖,其他配置不变,swagger访问地址使用:
http://ip:网关端口号/swagger-ui/index.html
网友评论