话不多说,,,先解释一波。
Swagger是现在前后端都比较喜欢的一个接口文档插件,服务端同学简单的编辑就可以给前端同学呈现出可以直接调试的接口文档,可以说是非常的6。现在微服务都是通过网关转发到各个服务上的,那么Swagger理所当然的也应该由网关代理,这篇文章主要就是记录一下由Gateway代理Swagger的方法。
gateway:
spring:
cloud:
gateway:
routes: #数组形式
- id: product-service #路由唯一标识
uri: lb://XXX_XXX_server #从nocas进行转发,所以这个名字是服务在nocas内注册的名字
order: 1 #优先级,数字越小优先级越高
predicates: #配置转发路径,前端访问路径统一加上XXX-server,网关判断转发对应的服务
- Path=/XXX-server/**
filters: #过滤器,请求在传递过程中通过过滤器修改
- StripPrefix=1 #去掉第一层前缀,转发给后续的路径
package com.home.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;
/**
* 网关转发swagger请求
*/
@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)));
}
}
package com.home.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;
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v3/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<>();
//取出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;
}
}
微服务:
package com.home.exception;
import com.home.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import springfox.documentation.spring.web.json.Json;
import java.util.*;
/**
* 重新封装swagger返回信息(需要网关代理的项目都需要重新封装,对单机请求没有影响)
*/
@Slf4j
@ControllerAdvice(basePackages = "springfox.documentation.oas.web")
public class SwaggerHandle implements ResponseBodyAdvice<Object> {
@Value("${spring.application.name}")
private String applictionName;
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object object, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (object == null) {
return null;
}
//获取请求头
HttpHeaders headers = serverHttpRequest.getHeaders();
Set<String> strings = headers.keySet();
//判断请求是否经过网关转发
boolean isForwarded = strings.contains("forwarded");
if (isForwarded) {
String x_forwarded_for = headers.get("x-forwarded-for").get(0);
String x_forwarded_proto = headers.get("x-forwarded-proto").get(0);
String x_forwarded_prefix = headers.get("x-forwarded-prefix").get(0);
String x_forwarded_port = headers.get("x-forwarded-port").get(0);
String x_forwarded_host = headers.get("x-forwarded-host").get(0);
//重新组装servers(总感觉方法有点low。。。)
Json jsons = (Json) object;
String value = jsons.value();
Map map = JsonUtil.toObject(value, Map.class);
List<Map<String, String>> servers = new ArrayList<>();
Map<String, String> server = new HashMap<>();
server.put("url", x_forwarded_proto + "://" + x_forwarded_host + x_forwarded_prefix);
server.put("description", applictionName);
servers.add(server);
map.put("servers", servers);
return new Json(JsonUtil.toJson(map));
} else {
return object;
}
}
}
这里面用到的工具类:JsonUtil