在微服务架构下,一般每一个微服务都会使用Swagger来管理咱们的接口文档,当微服务愈来愈多,接口查找管理无形中要浪费咱们很多时间,毕竟懒是程序员的美德。html
因为swagger2暂时不支持webflux 走了不少坑,完成这个效果感谢 @dreamlu @世言。java
经过访问网关的 host:port/swagger-ui.html,便可实现: pig聚合文档效果预览传送门git
经过右上角的Select a spec 选择服务模块来查看swagger文档程序员
获取到zuul配置的路由信息,主要到SwaggerResourceweb
/** * 参考jhipster * GatewaySwaggerResourcesProvider */ @Component @Primary public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvider { private final RouteLocator routeLocator; public RegistrySwaggerResourcesProvider(RouteLocator routeLocator) { this.routeLocator = routeLocator; } @Override public List<SwaggerResource> get() { List<SwaggerResource> resources = new ArrayList<>(); List<Route> routes = routeLocator.getRoutes(); routes.forEach(route -> { //受权不维护到swagger if (!StringUtils.contains(route.getId(), ServiceNameConstant.AUTH_SERVICE)){ resources.add(swaggerResource(route.getId(), route.getFullPath().replace("**", "v2/api-docs"))); } }); 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; } }
@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())) .filter(predicateDefinition -> !"pigx-auth".equalsIgnoreCase(routeDefinition.getId())) .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; } }
@Slf4j @Configuration @AllArgsConstructor public class RouterFunctionConfiguration { private final SwaggerResourceHandler swaggerResourceHandler; private final SwaggerSecurityHandler swaggerSecurityHandler; private final SwaggerUiHandler swaggerUiHandler; @Bean public RouterFunction routerFunction() { return RouterFunctions.route( .andRoute(RequestPredicates.GET("/swagger-resources") .and(RequestPredicates.accept(MediaType.ALL)), swaggerResourceHandler) .andRoute(RequestPredicates.GET("/swagger-resources/configuration/ui") .and(RequestPredicates.accept(MediaType.ALL)), swaggerUiHandler) .andRoute(RequestPredicates.GET("/swagger-resources/configuration/security") .and(RequestPredicates.accept(MediaType.ALL)), swaggerSecurityHandler); } }
@Override public Mono<ServerResponse> handle(ServerRequest request) { return ServerResponse.status(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON_UTF8) .body(BodyInserters.fromObject(swaggerResources.get())); } @Override public Mono<ServerResponse> handle(ServerRequest request) { return ServerResponse.status(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON_UTF8) .body(BodyInserters.fromObject( Optional.ofNullable(securityConfiguration) .orElse(SecurityConfigurationBuilder.builder().build()))); } @Override public Mono<ServerResponse> handle(ServerRequest request) { return ServerResponse.status(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON_UTF8) .body(BodyInserters.fromObject( Optional.ofNullable(uiConfiguration) .orElse(UiConfigurationBuilder.builder().build()))); }
经过以上配置,能够实现文档的参考和展现了,可是使用swagger 的 try it out 功能发现路径是路由切割后的路径好比:api
swagger 文档中的路径为: 主机名:端口:映射路径 少了一个 服务路由前缀,是由于展现handler 通过了 StripPrefixGatewayFilterFactory 这个过滤器的处理,原有的 路由前缀被过滤掉了!架构
return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .host("主机名:端口:服务前缀") //注意这里的主机名:端口是网关的地址和端口 .select() .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.any()) .build() .globalOperationParameters(parameterList);
swagger 在拼装URL 数据时候,会增长X-Forwarder-Prefix 请求头里面的信息为前缀app
经过如上分析,知道应该在哪里下手了吧,在 网关上追加一个请求头便可ide
@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, SwaggerProvider.API_URI)) { return chain.filter(exchange); } String basePath = path.substring(0, path.lastIndexOf(SwaggerProvider.API_URI)); ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build(); ServerWebExchange newExchange = exchange.mutate().request(newRequest).build(); return chain.filter(newExchange); }; } }
相对zuul的实现,核心逻辑都是同样,获取到配置路由信息,重写swaggerresource微服务
gateway的配置稍微麻烦,资源的提供handler,swagger url 重写的细节
源码获取:最新Spring Cloud 技术栈,基于Spring Cloud Finchley.RELEASE、oAuth2 实现的权限系统