前言:
我们经常在springboot单体项目中,集成swagger来整合接口文档;
但是在微服务springcloud项目下,业务模块众多,如果再像之前一样单独访问每个模块的 swagger-ui.html ,则非常麻烦。既然我们已经通过 nacos和gateway 实现统一访问,那我们也可以通过网关将所有的应用的swagger界面聚合起来。这样前端开发的时候只需要访问网关的swagger就可以,而不用访问每个应用的swagger。
先看一下项目架构:
整合Knife4j后的效果图:
废话不多说,直接上代码:
gateway-management
模块:
导入依赖:
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
添加相关配置类:
SwaggerHandler
:
package com.test.gateway.config;
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.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;
/**
* @author laz
*/
@RestController
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("/swagger-resources/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
/**
* 这个接口的作用是获取其他模块的数据
* 每次刷新接口文档的时候都会进行调用这个方法获取其他模块的数据
* @return
*/
@GetMapping("/swagger-resources")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
SwaggerHeaderFilter
:
package com.test.gateway.config;
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.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
private static final String HEADER_NAME = "X-Forwarded-Prefix";
private static final String URI = "/v2/api-docs";
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (!StringUtils.endsWithIgnoreCase(path,URI )) {
return chain.filter(exchange);
}
String basePath = path.substring(0, path.lastIndexOf(URI));
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
};
}
}
SwaggerResourceConfig
:
package com.test.gateway.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
route.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("**", "v2/api-docs"))));
});
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
log.info("name:{},location:{}",name,location);
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
consumer-management
和user-management
模块:
分别导入依赖:
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
Swagger2Config
配置:
package com.test.consumer.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.async.DeferredResult;
import springfox.documentation.builders.*;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.paths.RelativePathProvider;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import javax.servlet.ServletContext;
/**
* @description: swagger配置文件
**/
@Configuration
@EnableSwagger2
@EnableKnife4j
public class Swagger2Config
{
@Value("${spring.profiles.active}")
private String env;
@Value("${spring.application.name}")
private String serviceName;
@Value("${gateway.service.name}")
private String serviceNameForGateway;
@Bean
public Docket createDocket(ServletContext servletContext)
{
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.genericModelSubstitutes(DeferredResult.class)
.forCodeGeneration(true)
.pathMapping("/")
.select()
.build()
.apiInfo(new ApiInfoBuilder()
.title(serviceName + "接口文档")
.version("1.0")
.contact(new Contact("xxx","",""))
.license("XXX有限公司")
.build())
// 如果为生产环境,则不创建swagger
// .enable(!"real".equals(env));
.enable(true);
// 在knife4j前端页面的地址路径中添加gateway网关的项目名,解决在调试接口、发送请求出现404的问题
docket.pathProvider(new RelativePathProvider(servletContext)
{
@Override
public String getApplicationBasePath()
{
return "/" + serviceNameForGateway + super.getApplicationBasePath();
}
});
return docket;
}
}
然后分别启动gateway-management
,user-management
,consumer-management
三个服务。
浏览器访问:http://ip:gateway端口/doc.html
最终页面:
源码地址:https://gitee.com/lianaozhe/spring-cloud-nacos