前言:
在实际开发当中前后端对接的时候,经常会用到swagger,但是如果使用了路由的话,如果不用路由网关去分发swagger的话,前后端对解决的时候会很麻烦,微服务的接口增多对于对接的成本就会变高,那么这一节就写一写gatewaty转发swagger
代码配置:
具体微服务
需要用到swagger的微服务上添加swagger的依赖,为了方便我这边直接就使用上一章用到的那个test接口的nacos客户端的项目
<?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">
<parent>
<groupId>com.gitee.simons.cloud</groupId>
<artifactId>simons-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>simons-nacos</artifactId>
<name>${project.artifactId} [web]</name>
<packaging>jar</packaging>
<!-- 版本,字段关键字 -->
<properties>
<start-class>com.gitee.simons.nacos.NacosApplication</start-class>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--swagger相关 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>spring-boot</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>${start-class}</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!--解決持續集成無法找到main類 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${start-class}</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
主要就是加上swagger,由于swagger的配置版本越高,功能越全面这边建议无脑最新relase,反正不在生产环境上出现的东西,怎么方便怎么来。
接下来就是要加上一些跨域和swagger的配置,直接复制我的代码即可
swagger的配置类,要注意的是,return的docket里面有个basePackagex需要根据实际的包路径填写的
复制的时候注意一下即可
package com.gitee.simons.nacos.swagger.config;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.DocExpansion;
import springfox.documentation.swagger.web.OperationsSorter;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* SwaggerConfig api文档地址:/swagger-ui.html
*
* @author JSM
*/
@Configuration
public class SwaggerConfig {
@Value("${spring.application.name}")
private String applicationName;
@Bean
public Docket createRestApi() {
// 构造token给测试的时候填写
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
tokenPar.name("token").description("用户令牌(不需用户鉴权的不需要传)").modelRef(new ModelRef("string")).parameterType("header")
.required(false).build();
pars.add(tokenPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(defaultTitleInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.gitee.simons"))
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build().globalOperationParameters(pars);
}
/**
* 默认标题信息
*
* @return
*/
private ApiInfo defaultTitleInfo() {
// 大标题
return new ApiInfoBuilder().title("")
// 详细描述
.description("")
.contact(new Contact("", "", ""))
// 版本
.version("4.0")
.build();
}
@Bean
UiConfiguration uiConfig() {
return UiConfigurationBuilder.builder().docExpansion(DocExpansion.LIST).operationsSorter(OperationsSorter.ALPHA).build();
}
}
swagger的资源过滤配置
package com.gitee.simons.nacos.swagger.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* test
*
* @author: jsm
* @date: 2018/11/16 10:40
*
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 跨域支持
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("*")
.maxAge(3600)
.allowedHeaders("X-Requested-With", "Content-Type","token");
}
/**
* 添加静态资源--过滤swagger-api (开源的在线API文档)
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
全局跨域配置
package com.gitee.simons.nacos.swagger.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CrosAppConfig implements WebMvcConfigurer {
@Autowired
private CrosInterceptor crosInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 配置全局 的cros
registry.addInterceptor(crosInterceptor).addPathPatterns("/**/**");
}
}
跨域的详细信息设置
package com.gitee.simons.nacos.swagger.config;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class CrosInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//处理跨域名问题
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,token");
response.setHeader("Access-Control-Allow-Credentials", "true");
return true;
}
}
那么只要复制了这些文件之后,在applicaiton文件上面加上@EnableSwagger2标签就可以的了
package com.gitee.simons.nacos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan("com.gitee")
@EnableSwagger2
public class NacosApplication {
public static void main(String[] args) {
SpringApplication.run(NacosApplication.class, args);
}
}
配置完这些信息之后,启动application在浏览器输入http://localhost:8112/swagger-ui.html
就可以直接请求到swagger的页面
gateway转发配置
这一步之后具体的微服务就可以实现请求了,但是我们的目标是需要可以做到gateway转发, 那么应该还要再gateway项目下做一些操作
如果用过zuul做过路由转发的朋友,应该知道zuul是用的路由页面跳转的方式,这种方式得在serlvet的方式下才可以进行,但gateway是一个用netty框架为基础的服务,那么他就无法使用zuul的方式进行路由,但可以使用另外的方式,就是获取gateway路由参数中的服务,然后显示在select a spec上,通过请求swagger的v2/api-docs接口返回json数据,再放进swagger的页面中,大概这种思路
那么直接上配置
首先给gateway加上swagger的pom 依赖
<!--swagger相关 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
对swagger路由的配置
package com.sunmnet.mediaroom.gateway.config;
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 Sywd
*/
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
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(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("2.0");
return swaggerResource;
}
}
因为swagger打开页面的时候,会默认请求一系列的请求,这些请求会返回页面信息的基本构成内容,
但由于gateway上用的是webflux,swagger是无法获取到这些信息的,那么我们只能自己伪造一个这些接口并返回成功的空信息,以此绕过报错,然后具体资源信息通过转发的请求各个微服务中获取
swagger资源报错信息绕过
package com.gitee.simons.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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;
import java.util.Optional;
@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));
}
@GetMapping("/swagger-resources")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
@GetMapping("/")
public Mono<ResponseEntity> swaggerResourcesN() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
@GetMapping("/csrf")
public Mono<ResponseEntity> swaggerResourcesCsrf() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
配置完之后,就可以启动application,请求通过swagger路由获取到具体的信息了
image.png
网友评论