About pigX: The latest microservices scaffolding across the Web, Spring Cloud Finchley, oAuth2 best practices

In microservices architecture, usually every microservice will use Swagger to manage our interface document. As more and more microservices, interface lookup management will waste a lot of time. After all, laziness is the virtue of programmers.

Since Swagger2 temporarily does not support WebFlux to go a lot of pit, complete this effect thanks to @dreamlu @World.

Document aggregation effect

Pig aggregate document effect preview portal by accessing the gateway’s host:port/swagger-ui.html

View the Swagger document by selecting the service module by Select a Spec in the upper right corner

Pig’s Zuul core implementation

Obtain zuul configuration routing information, mainly to SwaggerResource

/ * * * reference GatewaySwaggerResourcesProvider jhipster * * /
@Component
@Primary
public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvider {
    private final RouteLocator routeLocator;
    public RegistrySwaggerResourcesProvider(RouteLocator routeLocator) {
        this.routeLocator = routeLocator;
    }
    
    @Override
    public List<SwaggerResource> get(a) {
        List<SwaggerResource> resources = new ArrayList<>();
        List<Route> routes = routeLocator.getRoutes();
        routes.forEach(route -> {
            // Authorization does not maintain to 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");
        returnswaggerResource; }}Copy the code

PigX’s Spring Cloud Gateway implementation

Inject route toSwaggerResource

@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(a) {
		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");
		returnswaggerResource; }}Copy the code

Provides Swagger external interface configuration

@Slf4j
@Configuration
@AllArgsConstructor
public class RouterFunctionConfiguration {
	private final SwaggerResourceHandler swaggerResourceHandler;
	private final SwaggerSecurityHandler swaggerSecurityHandler;
	private final SwaggerUiHandler swaggerUiHandler;

	@Bean
	public RouterFunction routerFunction(a) {
		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); }}Copy the code

Implementation of business Handler

	@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())));
	}
Copy the code

Swagger path conversion

Swagger’s try it Out function is used to find that the path is the path after the route cut. For example:

Swagger in the document path is: hostname: port: mapping path without a prefix service routing, because show handler after StripPrefixGatewayFilterFactory this filter processing, the original routing prefixes are filtered out.

Option 1: Manually maintain a prefix through Swagger’s host configuration

return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    .host("Host name: Port: service prefix")  // Note the hostname here: port is the address and port of the gateway
    .select()
    .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
    .paths(PathSelectors.any())
    .build()
    .globalOperationParameters(parameterList);
Copy the code

Option 2: Add x-Forwarded-prefix

Swagger Prefix the information in the x-forwarder-prefix request header when assembling URL data

@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();
			returnchain.filter(newExchange); }; }}Copy the code

conclusion

  • Compared with zuul implementation, the core logic is the same, get the configuration routing information, rewrite swaggerResource

  • Gateway configuration is a bit cumbersome, resources provide handler, Swagger URL rewriting details

  • My planet of Knowledge: “The cutting edge of Micro services” free micro services information sharing

  • Source code: based on Spring Cloud Finchley.RELEASE, oAuth2 implementation of the authority system