Gateway+Swagger2配置聚合文檔方式
Spring Cloud Gateway網(wǎng)關(guān)模塊聚合各微服務(wù)的Swagger接口文檔
效果如下圖
相關(guān)pom依賴
<!-- 網(wǎng)關(guān)路由代理 (僅網(wǎng)關(guān))--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>2.2.5.RELEASE</version> </dependency> <!--swagger2 (網(wǎng)關(guān)和服務(wù)端)--> <dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.7.0.RELEASE</version> </dependency>
Gateway配置
因?yàn)镾wagger暫不支持webflux項(xiàng)目,所以Gateway里不能配置SwaggerConfig
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; /** * 功能描述: 采用路由id做資源名稱,predicates.Path做映射的方式. * 如果需要自定義資源名稱,可以單獨(dú)配置一個(gè)集合(route.id,name),創(chuàng)建swaggerResource時(shí), * 通過(guò)route.id獲取name. * <p>例如: swagger: * route_names: * - name: 訂單 * id: order_route * - name: 訂單1 * id: order_route1 * spring: cloud: gateway: routes: * - id: order_route * - id: order_route1 * </p> * 重新Gateway的方法還沒(méi)研究出來(lái) - -|| * * @Author: * @Date: 2021-04-25 15:38 * @Version 1.0 **/ @Component @Primary public class Swagger2 implements SwaggerResourcesProvider { protected static final String API_URI = "/v2/api-docs";//固定后綴 private final RouteLocator routeLocator; private final GatewayProperties gatewayProperties; //資源集合 private static List<SwaggerResource> resources; //自定義資源名稱需要在這里初始化 public Swagger2(RouteLocator routeLocator, GatewayProperties gatewayProperties) { this.routeLocator = routeLocator; this.gatewayProperties = gatewayProperties; } @Override public List<SwaggerResource> get() { //做了一個(gè)簡(jiǎn)單緩存,沒(méi)仔細(xì)設(shè)計(jì),其實(shí)可有可無(wú)用處不大 if (resources == null) { resources = new ArrayList<>(); List<String> routes = new ArrayList<>(); routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); //結(jié)合配置的route-路徑(Path),和route過(guò)濾,只獲取有效的route節(jié)點(diǎn) 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; } }
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 @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)); } @GetMapping("") public Mono<ResponseEntity> swaggerResources() { //入口 return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); } }
swagger地址過(guò)濾器
使用swagger 的 try it out 功能發(fā)現(xiàn)路徑錯(cuò)誤,缺少服務(wù)器映射前綴的時(shí)候在用下面的過(guò)濾器.swagger 在拼裝URL 數(shù)據(jù)時(shí)候,會(huì)增加X-Forwarder-Prefix 請(qǐng)求頭里面的信息為前綴
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; @Component public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory { 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, Swagger2.API_URI)) { return chain.filter(exchange); } String basePath = path.substring(0, path.lastIndexOf(Swagger2.API_URI)); ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build(); ServerWebExchange newExchange = exchange.mutate().request(newRequest).build(); return chain.filter(newExchange); }; } }
配置文件
spring: cloud: gateway: routes: #路由的ID,沒(méi)有固定規(guī)則但要求唯一,簡(jiǎn)易配合服務(wù)名 - id: order_route #匹配后提供服務(wù)的路由地址 #uri: http://localhost:8001 #匹配后提供服務(wù)的路由地址,lb后跟提供服務(wù)的微服務(wù)的名,不要寫錯(cuò) uri: lb://order #斷言 predicates: #路徑相匹配的進(jìn)行路由 - Path=/order/** #過(guò)濾掉url里的city,例如http://ip:port/city/getCity -> http://ip:port/getCity #filters: # - RewritePath=/city/(?<segment>.*), /$\{segment} filters: # - SwaggerHeaderFilter - StripPrefix=1 #不要忘了
服務(wù)端
import org.springframework.beans.factory.annotation.Value; 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.ApiKey; import springfox.documentation.service.AuthorizationScope; import springfox.documentation.service.SecurityReference; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.ArrayList; import java.util.List; /** * 功能描述: Swagger2 接口文檔 * * @Author: * @Date: 2021-04-23 15:59 * @Version 1.0 **/ @Configuration @EnableSwagger2 public class Swagger2 { //是否開啟swagger,正式環(huán)境一般是需要關(guān)閉的,可根據(jù)springboot的多環(huán)境配置進(jìn)行設(shè)置 @Value(value = "${swagger.enabled}") private Boolean swaggerEnabled; //顯示文檔版本 @Value(value = "${swagger.version}") private String version; //標(biāo)題 @Value(value = "${swagger.title}") private String title; //描述信息 @Value(value = "${swagger.description}") private String description; //掃描接口位置 也可以配置到nacos中 private static final String base_package = "com.demo.controller"; @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2)// 指定api類型為swagger2 .apiInfo(apiInfo())// 用于定義api文檔匯總信息 // 是否開啟 .enable(swaggerEnabled).select() // 掃描的路徑包 .apis(RequestHandlerSelectors.basePackage(base_package)) // 指定路徑處理PathSelectors.any()代表所有的路徑 .paths(PathSelectors.any()).build() .pathMapping("/") .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); } //securitySchemes的ApiKey中增加一個(gè)名為“Authorization”,type為“header”的參數(shù) private List<ApiKey> securitySchemes() { List<ApiKey> apiKeyList = new ArrayList(); apiKeyList.add(new ApiKey("Authorization", "Authorization", "header")); return apiKeyList; } private List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; List<SecurityReference> securityReferences = new ArrayList<>(); securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); return securityReferences; } private List<SecurityContext> securityContexts() { List<SecurityContext> securityContexts = new ArrayList<>(); securityContexts.add( SecurityContext.builder() .securityReferences(defaultAuth()) .forPaths(PathSelectors.regex("^(?!auth).*$")) .build()); return securityContexts; } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(title)// 文檔頁(yè)標(biāo)題 .contact(new Contact("s","s.com","s@163.com"))// 詳細(xì)信息 .version(version)// 文檔版本號(hào) .termsOfServiceUrl("www.baidu.com")// 網(wǎng)站地址 .description(description) .build(); } } ```java
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Apache?SkyWalking?監(jiān)控?MySQL?Server?實(shí)戰(zhàn)解析
這篇文章主要介紹了Apache?SkyWalking?監(jiān)控?MySQL?Server?實(shí)戰(zhàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09springBoot集成redis的key,value序列化的相關(guān)問(wèn)題
這篇文章主要介紹了springBoot集成redis的key,value序列化的相關(guān)問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08java使用randomaccessfile在文件任意位置寫入數(shù)據(jù)
Java在文件任意位置寫入數(shù)據(jù)可以使用RandomAccessFile方法來(lái)完成,下面看一個(gè)簡(jiǎn)單的示例就明白了2014-01-01Java替換中使用正則表達(dá)式實(shí)現(xiàn)中間模糊匹配的方法
今天小編就為大家分享一篇Java替換中使用正則表達(dá)式實(shí)現(xiàn)中間模糊匹配的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Java多線程之線程通信生產(chǎn)者消費(fèi)者模式及等待喚醒機(jī)制代碼詳解
這篇文章主要介紹了Java多線程之線程通信生產(chǎn)者消費(fèi)者模式及等待喚醒機(jī)制代碼詳解,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10