davidvera
davidvera

Reputation: 1499

Centralize swaggers in api gateway returns "Failed to load remote configuration."

I created a discovery server using Eureka, I added a gateway and 2 webservices. All I added 2 webservices and a gateway. My service do correctly register to Eureka. I am using :

I want to have all swagger documentation available in the gateway.

On webservices I added the following dependencies:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.3.0</version>
</dependency>

On each I also add OpenApiDefinition annotation:

...
@OpenAPIDefinition(info =
    @Info(
            title = "User Management and authentication Webservice",
            description = "This web service allow register, log in and update user profile",
            version = "1.0",
            contact = @Contact(
                    name = "Technical Service",
                    url = "https://www.******/",
                    email = "****@****"
            ),
            termsOfService = "https://www.*****",
            license = @License(name =  "Apache 2.0", url = "http://www.apache.org/licenses/LICENSE-2.0")
))
public class UserWsApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserWsApplication.class, args);
    }
}

On user webservice I added the following setting (extract):

server.port=${PORT:0}
spring.application.name=users-ws
springdoc.show-actuator=false

The second web service:

server.port=${PORT:0}
spring.application.name=licences-ws
springdoc.show-actuator=false

On the gateway, I added the following dependency

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webflux-api</artifactId>
    <version>2.3.0</version>
</dependency>

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
    <version>2.3.0</version>
</dependency>

I configured my routes

# remote openapi
spring.cloud.gateway.routes[0].id=openapi
spring.cloud.gateway.routes[0].uri = http://localhost:${server.port}
spring.cloud.gateway.routes[0].predicates[0]=Path=/v3/api-docs/**
spring.cloud.gateway.routes[0].filters[0]=RewritePath=/v3/api-docs/(?<segment>.*), /$\{segment}/v3/api-docs


# status public page
spring.cloud.gateway.routes[1].id=users-ws-status
spring.cloud.gateway.routes[1].uri = lb://users-ws
spring.cloud.gateway.routes[1].predicates[0]=Path=/users/status
spring.cloud.gateway.routes[1].predicates[1]=Method=GET
spring.cloud.gateway.routes[1].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[1].filters[1]=RewritePath=/users-ws/users/status, /users/status

# user registration routes do not need filter
spring.cloud.gateway.routes[2].id=users-ws
spring.cloud.gateway.routes[2].uri = lb://users-ws
spring.cloud.gateway.routes[2].predicates[0]=Path=/users
spring.cloud.gateway.routes[2].predicates[1]=Method=POST
spring.cloud.gateway.routes[2].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[2].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

spring.cloud.gateway.routes[3].id = users-ws-login
spring.cloud.gateway.routes[3].uri = lb://users-ws
spring.cloud.gateway.routes[3].predicates[0]=Path=/users/login
spring.cloud.gateway.routes[3].predicates[1]=Method=POST
spring.cloud.gateway.routes[3].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[3].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

# reset request
spring.cloud.gateway.routes[4].id = users-ws-auth-reset-request
spring.cloud.gateway.routes[4].uri = lb://users-ws
spring.cloud.gateway.routes[4].predicates[0]=Path=/users/password-reset-request
spring.cloud.gateway.routes[4].predicates[1]=Method=POST
spring.cloud.gateway.routes[4].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[4].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

# reset password validation: we send get request with request param named token
spring.cloud.gateway.routes[5].id = users-ws-auth-reset-validation
spring.cloud.gateway.routes[5].uri = lb://users-ws
spring.cloud.gateway.routes[5].predicates[0]=Path=/users/password-reset
#spring.cloud.gateway.routes[5].predicates[1]=Query=token
spring.cloud.gateway.routes[5].predicates[1]=Method=POST
spring.cloud.gateway.routes[5].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[5].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

# user management routes do  need filter
spring.cloud.gateway.routes[6].id=users-management-ws
spring.cloud.gateway.routes[6].uri=lb://users-ws
spring.cloud.gateway.routes[6].predicates[0]=Path=/users/**
spring.cloud.gateway.routes[6].predicates[1]=Method=GET,PUT,DELETE
spring.cloud.gateway.routes[6].predicates[2]=Header=Authorization, Bearer (.*)
spring.cloud.gateway.routes[6].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[6].filters[1]=AuthorizationHeaderFilter
spring.cloud.gateway.routes[6].filters[2]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

# configuring filter for actuator
spring.cloud.gateway.routes[7].id = users-ws-actuator
spring.cloud.gateway.routes[7].uri = lb://users-ws
spring.cloud.gateway.routes[7].predicates[0]=Path=/users-ws/actuator/**
spring.cloud.gateway.routes[7].predicates[1]=Method=GET
spring.cloud.gateway.routes[7].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[7].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}


# user registration routes do not need filter
spring.cloud.gateway.routes[8].id=licences-ws
spring.cloud.gateway.routes[8].uri = lb://licences-ws
spring.cloud.gateway.routes[8].predicates[0]=Path=/users/**
spring.cloud.gateway.routes[8].predicates[1]=Method=POST,GET
spring.cloud.gateway.routes[8].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[8].filters[1]=RewritePath=/licences-ws/(?<segment>.*), /$\{segment}

I also added then following settings for open api doc

springdoc.swagger-ui.use-root-path=true
springdoc.api-docs.enabled=true
#springdoc.api-docs.path=/api-docs
springdoc.swagger-ui.enabled=true
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.swagger-ui.config-url=/v3/api-docs/swagger-config

springdoc.swagger-ui.urls[0]=/v3/api-docs
springdoc.swagger-ui.urls[0].name=API Gateway Service
springdoc.swagger-ui.urls[0].display-name=API Gateway Service

#springdoc.swagger-ui.urls[0].primaryName=API Gateway Service
springdoc.swagger-ui.urls[1].name=users
springdoc.swagger-ui.urls[1]=/v3/api-docs/users-ws
springdoc.swagger-ui.urls[1].display-name=Users Service

springdoc.swagger-ui.urls[2].name=licences
springdoc.swagger-ui.urls[2]=/v3/api-docs/licences-ws
springdoc.swagger-ui.urls[2].display-name=Licences Service

server.forward-headers-strategy=framework

And finally added the following configuration bean

@Bean
public List<GroupedOpenApi> apis(RouteDefinitionLocator locator) {
    List<GroupedOpenApi> groups = new ArrayList<>();
    List<RouteDefinition> definitions = locator
            .getRouteDefinitions()
            .collectList()
            .block();

    assert definitions != null;
    definitions.forEach(definition ->
            System.out.println("id: " + definition.getId() + "  " +
                    definition.getUri().toString()));

    System.out.println("**************************");
    definitions.stream()
            .filter(routeDefinition -> routeDefinition.getId()
                    .matches(".*-ws"))
            .forEach(routeDefinition -> {
                String name = routeDefinition
                        .getId()
                        .replaceAll("-ws", "");
                System.out.println("Name >> "  + name);
                groups.add(GroupedOpenApi.builder()
                        .pathsToMatch("/" + name + "/**")
                        .group(name)
                        .build());
            });
    // we add services without ws in name
    return groups;
}

When running my application:

  1. I can reach my user web service swagger when i use the user webservice: http://127.0.0.1:25682/swagger-ui/index.html
  2. When I reach the centralized swagger documentation here: http://localhost:8765/webjars/swagger-ui/index.html I have a "failed to load remote configuration"

enter image description here

  1. When I reach the following URL (http://127.0.0.1:8765/users-ws/swagger-ui/index.html) I have "failed to load remote configuration"
  2. I notice that http://localhost:8765/v3/api-docs/swagger-config returns a 404 error too

enter image description here

My issue is:

  1. How can i centralize all my swaggers in the spring gateway ?
  2. Why do i have webjars in my URL in the gateway (without webjars i have a 404)

Part of my openAPI implementation comes from here: https://github.com/piomin/sample-spring-microservices-new

Upvotes: 1

Views: 563

Answers (1)

davidvera
davidvera

Reputation: 1499

I finally solved a few days ago. I simply added the OpenApiDefinition annotation with servers param on each micro services that register to Eureka:

@OpenAPIDefinition(
    servers = { @Server(url = "/setups", description = "Default URL") },
    info = @Info(
            title = "...",
            version = "1.0.0",
            description = "..."))

Upvotes: 0

Related Questions