前言:
在實際開發(fā)當中前后端對接的時候吗讶,經(jīng)常會用到swagger,但是如果使用了路由的話曹抬,如果不用路由網(wǎng)關(guān)去分發(fā)swagger的話溉瓶,前后端對解決的時候會很麻煩急鳄,微服務(wù)的接口增多對于對接的成本就會變高,那么這一節(jié)就寫一寫gatewaty轉(zhuǎn)發(fā)swagger
代碼配置:
具體微服務(wù)
需要用到swagger的微服務(wù)上添加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>
<!-- 版本疾宏,字段關(guān)鍵字 -->
<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相關(guān) -->
<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>
<!--解決持續(xù)集成無法找到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坎藐,反正不在生產(chǎn)環(huán)境上出現(xiàn)的東西,怎么方便怎么來哼绑。
接下來就是要加上一些跨域和swagger的配置岩馍,直接復制我的代碼即可
swagger的配置類,要注意的是抖韩,return的docket里面有個basePackagex需要根據(jù)實際的包路徑填寫的
復制的時候注意一下即可
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() {
// 構(gòu)造token給測試的時候填寫
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
tokenPar.name("token").description("用戶令牌(不需用戶鑒權(quán)的不需要傳)").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");
}
/**
* 添加靜態(tài)資源--過濾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("/**/**");
}
}
跨域的詳細信息設(shè)置
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轉(zhuǎn)發(fā)配置
這一步之后具體的微服務(wù)就可以實現(xiàn)請求了,但是我們的目標是需要可以做到gateway轉(zhuǎn)發(fā), 那么應(yīng)該還要再gateway項目下做一些操作
如果用過zuul做過路由轉(zhuǎn)發(fā)的朋友掠械,應(yīng)該知道zuul是用的路由頁面跳轉(zhuǎn)的方式,這種方式得在serlvet的方式下才可以進行顽馋,但gateway是一個用netty框架為基礎(chǔ)的服務(wù),那么他就無法使用zuul的方式進行路由幌羞,但可以使用另外的方式寸谜,就是獲取gateway路由參數(shù)中的服務(wù),然后顯示在select a spec上属桦,通過請求swagger的v2/api-docs接口返回json數(shù)據(jù)熊痴,再放進swagger的頁面中,大概這種思路
那么直接上配置
首先給gateway加上swagger的pom 依賴
<!--swagger相關(guān) -->
<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打開頁面的時候地啰,會默認請求一系列的請求愁拭,這些請求會返回頁面信息的基本構(gòu)成內(nèi)容讲逛,
但由于gateway上用的是webflux亏吝,swagger是無法獲取到這些信息的,那么我們只能自己偽造一個這些接口并返回成功的空信息盏混,以此繞過報錯蔚鸥,然后具體資源信息通過轉(zhuǎn)發(fā)的請求各個微服務(wù)中獲取
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路由獲取到具體的信息了