Cloud Native 13 min read

How to Aggregate Microservice APIs with Spring Cloud Gateway and Knife4j

Learn to unify API documentation across multiple Spring Cloud microservices by configuring Nacos, Spring Cloud Gateway, and Knife4j, covering prerequisite setup, service definitions, detailed implementation steps, Swagger resource aggregation, UI switching, and complete code examples for user, order, and gateway services.

macrozheng
macrozheng
macrozheng
How to Aggregate Microservice APIs with Spring Cloud Gateway and Knife4j

Prerequisite Knowledge

We use Nacos as the service registry, Spring Cloud Gateway as the gateway, and Knife4j to generate API documentation. Refer to the linked articles for details on these technologies.

Application Architecture

The ideal solution is a gateway that serves as a unified entry point, aggregating the API documentation of all microservices.

Related services:

micro-knife4j-gateway: gateway service that aggregates API docs and includes the UI package.

micro-knife4j-user: user service, a regular API service without the UI package.

micro-knife4j-order: order service, a regular API service without the UI package.

Specific Implementation

We will build the user service, order service, and gateway service step by step.

micro-knife4j-user

Integrate Knife4j into the user service in three steps.

Add the required dependencies in

pom.xml

:

<code>&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.github.xiaoymin&lt;/groupId&gt;
        &lt;artifactId&gt;knife4j-micro-spring-boot-starter&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;</code>

Add Nacos configuration in

application.yml

:

<code>server:
  port: 9501
spring:
  profiles:
    active: dev
  application:
    name: micro-knife4j-user
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
management:
  endpoints:
    web:
      exposure:
        include: "*"</code>

Add Swagger configuration and enable Knife4j:

<code>/**
 * Swagger API related configuration
 */
@Configuration
@EnableSwagger2
@EnableKnife4j
public class Swagger2Config {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.macro.cloud.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("micro-knife4j-user")
                .description("用户服务API文档")
                .contact("macro")
                .version("1.0")
                .build();
    }
}</code>

micro-knife4j-order

The order service follows the same steps as the user service.

micro-knife4j-gateway

Add dependencies for the gateway and Knife4j UI:

<code>&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
        &lt;artifactId&gt;spring-cloud-starter-gateway&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.github.xiaoymin&lt;/groupId&gt;
        &lt;artifactId&gt;knife4j-spring-boot-starter&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;</code>

Configure routing and Nacos in

application.yml

:

<code>server:
  port: 9201
spring:
  profiles:
    active: dev
  application:
    name: micro-knife4j-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        - id: user-service
          uri: lb://micro-knife4j-user
          predicates:
            - Path=/user-service/**
          filters:
            - StripPrefix=1
        - id: order-service
          uri: lb://micro-knife4j-order
          predicates:
            - Path=/order-service/**
          filters:
            - StripPrefix=1
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true</code>

Add Swagger resource configuration to aggregate

api-docs

from downstream services:

<code>/**
 * Swagger resource configuration
 * Created by macro on 2020/7/9.
 */
@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {

    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        // Get all route IDs
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        // Filter routes defined in configuration, extract Path predicates, build api-docs path
        gatewayProperties.getRoutes().stream()
            .filter(routeDefinition -> routes.contains(routeDefinition.getId()))
            .forEach(route -> {
                route.getPredicates().stream()
                    .filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
                    .forEach(predicateDefinition -> resources.add(
                        swaggerResource(route.getId(),
                            predicateDefinition.getArgs()
                                .get(NameUtils.GENERATED_NAME_PREFIX + "0")
                                .replace("**", "v2/api-docs"))));
            });
        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location) {
        log.info("name:{},location:{}", name, location);
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion("2.0");
        return swaggerResource;
    }
}
</code>

Explain the

api-docs

endpoint, which returns JSON data used by Swagger UI. Example URL:

http://localhost:9201/user-service/v2/api-docs

.

Swagger API docs JSON example
Swagger API docs JSON example

Customize Swagger nodes via a controller:

<code>/**
 * Custom Swagger configuration endpoints
 * Created by macro on 2020/7/9.
 */
@RestController
public class SwaggerHandler {

    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;

    @Autowired(required = false)
    private UiConfiguration uiConfiguration;

    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }

    /** Swagger security configuration, supports oauth and apiKey */
    @GetMapping("/swagger-resources/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),
                HttpStatus.OK));
    }

    /** Swagger UI configuration */
    @GetMapping("/swagger-resources/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()),
                HttpStatus.OK));
    }

    /** Swagger resources configuration, provides api-docs info of each service */
    @GetMapping("/swagger-resources")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just(new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK));
    }
}
</code>

Demo

Start the Nacos registry, then start the

micro-knife4j-user

,

micro-knife4j-order

, and

micro-knife4j-gateway

services. Access the aggregated API documentation at

http://localhost:9201/doc.html

and switch between services using the UI component.

Gateway API docs UI
Gateway API docs UI

If you prefer the original Swagger UI, remove Knife4j dependencies and add Swagger dependencies as shown below, then restart all services and visit

http://localhost:9201/swagger-ui.html

.

<code>&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.github.xiaoymin&lt;/groupId&gt;
        &lt;artifactId&gt;knife4j-micro-spring-boot-starter&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.github.xiaoymin&lt;/groupId&gt;
        &lt;artifactId&gt;knife4j-spring-boot-starter&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;io.springfox&lt;/groupId&gt;
        &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
        &lt;version&gt;2.9.2&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;io.springfox&lt;/groupId&gt;
        &lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
        &lt;version&gt;2.9.2&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;io.swagger&lt;/groupId&gt;
        &lt;artifactId&gt;swagger-models&lt;/artifactId&gt;
        &lt;version&gt;1.6.0&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;io.swagger&lt;/groupId&gt;
        &lt;artifactId&gt;swagger-annotations&lt;/artifactId&gt;
        &lt;version&gt;1.6.0&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;</code>
Swagger UI after switching
Swagger UI after switching

Conclusion

Comparing Knife4j with native Swagger in a microservice setup confirms that Knife4j is an enhanced UI implementation of springfox‑swagger, fully adhering to the usage patterns of springfox‑swagger.

MicroservicesAPI DocumentationgatewaySpring CloudSwaggerKnife4j
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.