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.
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><dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
</dependency>
</dependencies></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><dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
</dependencies></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-docsfrom 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-docsendpoint, which returns JSON data used by Swagger UI. Example URL:
http://localhost:9201/user-service/v2/api-docs.
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-gatewayservices. Access the aggregated API documentation at
http://localhost:9201/doc.htmland switch between services using the UI component.
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><dependencies>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies></code>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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.