Search code examples
swaggerspring-cloud-gatewayspringfox

Cannot access api-docs of microservice from spring cloud gateway. Failed to load API definition


I'm trying to link my microservices to my gateway. , but I'm not able to access the api-docs of my microservice through the gateway.

Error from Swagger-UI:

Failed to load API definition
Fetch error
Not Found http://localhost:8080/microservice/v2/api-docs

Swagger version:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

I can GET the api-docs through the the microservice directly (port 8081):

http://localhost:8081/v2/api-docs

But I'm not able to do it through the gateway (port 8080):

http://localhost:8080/microservice/v2/api-docs

Microservice's properties:

spring:
  application:
    name: microservice

server:
  port: 5082
...

Gateway's properties:

server:
  port: 2443

spring:
  cloud:
    gateway:
      routes:
      - id: microservice
        uri: lb://microservice
        predicates:
        - Path=/microservice/**
...

The workaround that I've found is to add a @GetMapping in my Controller.java to point the URL specifically to the api-docs (but I'm pretty sure this is not the proper solution).

WebClient webClient;

@ResponseBody
@GetMapping(value = "/v2/api-docs")
public String getApiDocs()
{
    factory = new DefaultUriBuilderFactory("http://localhost:8081");
    factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
    
    this.webClient = WebClient
              .builder()
              .uriBuilderFactory(factory)
              .build();

    return webClient.get()
        .uri("/v2/api-docs")
        .retrieve()
        .bodyToMono(String.class)
        .block();
}

FYI:

  • The microservice and gateway are both registered with Eureka.
  • I can access the api-docs of the gateway just fine, but not the api-docs of the microservice.

Solution

  • The issue is that the gateway is not configured to load swagger associated resources (UI, config files). I will use in the explanation gateway config using @Configuration class, but probably it can be used in the properties file as well.

    1. Specify for each service a custom Swagger API path. So it can be used to configure routing in the gateway in the next steps. It will add prefix /microservice1-api in the path for the swagger UI URL and swagger JSON configuration file.
    springdoc:
          api-docs:
            path: /microservice1-api
          swagger-ui:
            path: /api.html
    
    1. Specify the route to each prefix in the gateway config. So it will be properly route paths associated with service swagger resources.
        RouteLocatorBuilder.Builder apiDocJsonRoutes(RouteLocatorBuilder.Builder builder) {
        //uri can be actual URI or load balancer path
                return builder
                        .route(p -> p.path( "/microservice1-api/**").uri("microservice1"))
                        .route(p -> p.path( "/microservice2-api/**").uri("microservice2"))
                        .route(p -> p.path("/microservice3-api/**").uri("microservice3"));
            }
    
    1. You have to define routing for /swagger-ui/index.html path to proper service. In this case path to service is recognized by URL parameter configUrl. After Step 1 if you open swagger in the browser, you will see that at the end of address line you have parameter ?configUrl=/microservice1-api/swagger-config. So if this parameter value has prefix associated with a specific service, you can use it for routing.
    RouteLocatorBuilder.Builder apiDocUIRoutes(RouteLocatorBuilder.Builder builder) {
            return builder
                    .route(p -> swaggerUi(p, "microservice1", "/microservice1-api/swagger-config"))
                    .route(p -> swaggerUi(p, "microservice2", "/microservice2-api/swagger-config"))
                    .route(p -> swaggerUi(p, "microservice3", "/microservice3-api/swagger-config"));
        }
    
    
        private Buildable<Route> swaggerUi(PredicateSpec p, String service, String expectedValue) {
            return p.path("/swagger-ui/index.html").and().query("configUrl", expectedValue)
                    .uri(service);
        }
    
    1. Open API in the browser using http://gateway_path/microservice_route/api.html