本文共 9620 字,大约阅读时间需要 32 分钟。
在springboot的单体工程中,整合swagger接口文档我们已经做好.现在加入了gateway网关了,所有的请求要是先经过gateway网关,gateway网关再路由转发到对应的微服务上.那么gateway整合所有微服务的swagger接口文档势在必行.
swagger接口文档的本质是,swagger的包中有html界面,也有实现了的接口,界面上访问这些接口获取到有所有接口的信息,并且模拟http请求.打开swagger的html界面,我们就可以看到这些接口.
思路其实还是简单的,页面上的数据来源于4个接口,我们在gateway中对这四个接口该重写的重写,响应的数据中该替换的替换就行了.
io.springfox springfox-swagger2 io.swagger swagger-models io.springfox springfox-swagger-ui io.swagger swagger-models
package com.ccm.gateway.controller;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;/** * @Author: ccm * @CreatedDateTime: 2020/8/5 10:02 * @Description: 重写swagger页面上的几个接口控制层 */@RestController@RequestMappingpublic class SwaggerController { @Autowired(required = false) private SecurityConfiguration securityConfiguration; @Autowired(required = false) private UiConfiguration uiConfiguration; private final SwaggerResourcesProvider swaggerResources; @Autowired public SwaggerController(SwaggerResourcesProvider swaggerResources) { this.swaggerResources = swaggerResources; } @GetMapping("/swagger-resources/configuration/security") public Mono> securityConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping("/swagger-resources/configuration/ui") public Mono > uiConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping("/swagger-resources") public Mono swaggerResources() { return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); } @GetMapping(value = "/") public Mono swaggerResourcesRoot() { return Mono.just((new ResponseEntity<>(HttpStatus.OK))); } @GetMapping("/csrf") public Mono swaggerResourcesCROF() { return Mono.just((new ResponseEntity<>(HttpStatus.OK))); }}
package com.ccm.gateway.service.impl;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: ccm * @CreatedDateTime: 2020/8/5 10:51 * @Description: gateway整合swagger,中获取swaggerresource的实现 */@AllArgsConstructor@Primary //容器中两个相同的Bean,优先注入,保证注入的不是默认的实现@Componentpublic class SwaggerResourcesImpl implements SwaggerResourcesProvider { public static final String API_URI = "/v2/api-docs"; private final RouteLocator routeLocator; private final GatewayProperties gatewayProperties; @Override public Listget() { List resources = new ArrayList<>(); List routes = new ArrayList<>(); //取出gateway的route routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); //结合配置的route-路径(Path),和route过滤,只获取有效的route节点 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; }}
上一篇文章中,已经介绍了gateway过滤器的三种实现方式.GlobalFilter、GatewayFilter和AbstractGatewayFilterFactory,这里修改basePath是作用在所有路由,所以我选择使用GlobalFilter更加简洁.
package com.ccm.gateway.filter;import com.ccm.gateway.service.impl.SwaggerResourcesImpl;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang.StringUtils;import org.reactivestreams.Publisher;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.core.io.buffer.DataBufferFactory;import org.springframework.core.io.buffer.DataBufferUtils;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.http.server.reactive.ServerHttpResponseDecorator;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;import java.nio.charset.Charset;import java.util.Arrays;/** * @Author: ccm * @CreatedDateTime: 2020/08/05 10:15 * @Description: gateway整合swagger2替换basepath路径 */@Slf4j@Componentpublic class SwaggerBasePathFilter implements GlobalFilter { @Override public Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) { //判断是否需要过滤 String path = exchange.getRequest().getURI().getPath(); if(StringUtils.endsWithIgnoreCase(path, SwaggerResourcesImpl.API_URI)) { //为swagger2视图请求接口的地址 ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { @Override public Mono writeWith(Publisher body) { if (body instanceof Flux) { Flux fluxBody = (Flux ) body; return super.writeWith(fluxBody.map(dataBuffer -> { // probably should reuse buffers byte[] content = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(content); //释放掉内存 DataBufferUtils.release(dataBuffer); //s就是response的值,想修改、查看就随意而为了 String serverName = Arrays.stream(path.split("/")).filter(StringUtils::isNotBlank).findFirst().get(); content = new String(content, Charset.forName("UTF-8")).replaceFirst("\"basePath\" ?: ?\"[^\"]*\"", "\"basePath\":\"/" + serverName + "\"").getBytes(); byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes(); return bufferFactory.wrap(uppedContent); })); } // if body is not a flux. never got there. return super.writeWith(body); } }; // replace response with decorator return chain.filter(exchange.mutate().response(decoratedResponse).build()); } return chain.filter(exchange); }}
server: port: 200 #服务端口spring: application: name: gateway #服务名称 cloud: nacos: discovery: server-addr: 47.96.131.185:8848 #nacos服务的注册和发现地址 #gateway组件相关配置 gateway: discovery: locator: enabled: true #开启基于服务的注册和发现的路由转发,默认轮询模式 routes: #路由配置 - id: server-user #路由名称,不配默认为UUID uri: lb://server-user #满足断言的路由到此服务 predicates: #为一个数组,每个规则为并且的关系 - Path=/api-user/** #断言表达式,如果args不写key的,会自动生成一个id,如下会生成一个xxx0的key,值为/foo/* filters: #请求路由转发前执行的filter,为数组 - StripPrefix=1 #缩写,和name=StripPrefix,args,参数=1是一个意思,该过滤器为路由转发过滤去 - id: server-basic #路由名称,不配默认为UUID uri: lb://server-basic #满足断言的路由到此服务 predicates: #为一个数组,每个规则为并且的关系 - Path=/api-basic/** #断言表达式,如果args不写key的,会自动生成一个id,如下会生成一个xxx0的key,值为/foo/* filters: #请求路由转发前执行的filter,为数组 - StripPrefix=1 #缩写,和name=StripPrefix,args,参数=1是一个意思,该过滤器为路由转发过滤去
您的点赞、收藏、转发和关注是我持续创作的动力!
源码地址:
转载地址:http://lktli.baihongyu.com/