I user Spring Cloud Gateway witch is based on Webflux, in my MicroService envoirement every Service has a own Swagger with UI... No problems so far. Now i would centralize all Swagger to one in my Gateway, i found tons of example code for the Zuul Gateway... I think this would work. But cant find any example for the spring.cloud.Gateway witch is base on the reactive Webflux interface, with the 'springfox:springfox-spring-webflux:3.0.0-SNAPSHOT' dependencie i get a SwaggerUI but i can not centralize all of my Swagger from the other MicroService i dont know how to configure the SwaggerResourcesProvider...
I dont know if i should do an own MicroService (no Webflux) to handle all Swaggers, i think this would not be a problem. The Problem is the WebFlux thing :-)
Maybe someone has a recommendation should i make an own MicroService (this should work but I'm not sure if this is over the top). Or someone has a solution for my Problem.
I've run into the same issue and I didn't find a clean solution because there hasn't integration between Swagger
and Webflux
yet.
On the other hand, I succeed to set up a minimal configuration to create a proxy for swagger.
First you need to create a controller exposing Swagger
API and ressources :
@Controller
public class SwaggerController {
private final JsonSerializer jsonSerializer;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerController(JsonSerializer jsonSerializer, SwaggerResourcesProvider swaggerResources) {
this.jsonSerializer = jsonSerializer;
this.swaggerResources = swaggerResources;
}
@RequestMapping({"/swagger-resources/configuration/security"})
@ResponseBody
public ResponseEntity<SecurityConfiguration> securityConfiguration() {
return ResponseEntity.ok(SecurityConfigurationBuilder.builder().build());
}
@RequestMapping({"/swagger-resources/configuration/ui"})
@ResponseBody
public ResponseEntity<UiConfiguration> uiConfiguration() {
return ResponseEntity.ok(UiConfigurationBuilder.builder().build());
}
@RequestMapping({"/swagger-resources"})
@ResponseBody
public ResponseEntity<List<SwaggerResource>> swaggerResources() {
return ResponseEntity.ok(this.swaggerResources.get());
}
@RequestMapping(
value = {"/v2/api-docs"},
method = {RequestMethod.GET},
produces = {"application/json", "application/hal+json"}
)
@ResponseBody
public ResponseEntity<Json> getDocumentation() {
Swagger swagger = new Swagger();
return ResponseEntity.ok(this.jsonSerializer.toJson(swagger));
}
}
Next you need a swagger configuration to create Swagger ressources from your microservices :
package com.stackoverflow.gateway.config;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import springfox.documentation.spring.web.json.JacksonModuleRegistrar;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@Configuration
public class SwaggerConfig {
private final GatewayProperties properties;
public SwaggerConfig(GatewayProperties properties) {
this.properties = properties;
}
@Bean
public JsonSerializer jsonSerializer(List<JacksonModuleRegistrar> moduleRegistrars) {
return new JsonSerializer(moduleRegistrars);
}
@Primary
@Bean
@Lazy
public SwaggerResourcesProvider swaggerResourcesProvider() {
return () -> properties.getRoutes().stream()
.map(route -> createResource(route.getId(), getRouteLocation(route), "2.0"))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
// You will certainly need to edit this
private String getRouteLocation(RouteDefinition route) {
return Optional.ofNullable(route.getPredicates().get(0).getArgs().values().toArray()[0])
.map(String::valueOf)
.map(s -> s.replace("*", ""))
.orElse(null);
}
private SwaggerResource createResource(String name, String location, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location + "/v2/api-docs");
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
I think this solution will not work if you use a Service Discovery server but for my case I don't need to because I have a config for spring.cloud.gateway.routes
:
- id: api-client
uri: lb://client
predicates:
- Path=/api/client/**
This can easily adapted to fetch service location from Eureka or Consul.
Let me know if it works. And if someone found another alternative I'm interested.
I used spring fox with spring cloud gateway to create the swagger UI.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
@Primary
@Configuration
public class SwaggerConfig implements SwaggerResourcesProvider {
@Autowired
private RouteLocator routeLocator;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
// I used service names with lowercase hence the toLowerCase.
// Remove the toLowerCase if you use service name with caps as is default with eureka.
routeLocator.getRoutes().subscribe(route -> {
String name = route.getId().split("_")[1];
resources.add(swaggerResource(name, "/" + name.toLowerCase() + "/v3/api-docs", "1.0"));
});
return resources;
}
private SwaggerResource swaggerResource(final String name, final String location,
final String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With