1. Add POM dependencies
1.1. Under the pom
Add unified version management
<properties>
<knife4j.version>3.0.3</knife4j.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
Copy the code
1.2. The gateway of pom
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
Copy the code
1.3. Pom for other services
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
</dependency>
Copy the code
The difference from Gateway is that this package has less UI dependence on Knife4J-Spring-UI.
Configure the Gateway service
Swagger core configuration Docket
package com.holland.gateway.swagger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
@Value("${spring.application.name}")
private String name;
@Bean
public Docket defaultApi2(a) {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(groupApiInfo(name))
// Group name Do not open group if you want gateway to be recorded to Swagger
//.groupname (" version 2.x ")
.select()
// Specify the Controller scan package path
.apis(RequestHandlerSelectors.basePackage("com.holland." + name + ".controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo groupApiInfo(String name) {
return new ApiInfoBuilder()
.title("Back-end interface Documentation -" + name)
.description("
description
")
.termsOfServiceUrl("N")
.contact(new Contact("HollanZang"."https://juejin.cn/user/352263461681214"."[email protected]"))
.version("1.0") .build(); }}Copy the code
2.2. Configure swagger interfaces
package com.holland.gateway.swagger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;
import java.util.Optional;
@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;
}
@GetMapping("/swagger-resources/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources")
publicMono<ResponseEntity<? >> swaggerResources() {return Mono.just((newResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); }}Copy the code
2.3. Configure a filter
2.3.1 Official Demo practice
package com.xiaominfo.swagger.service.doc.config;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
/ * * *@author fsl
* @description: SwaggerHeaderFilter
* @dateIn the 2019-06-03 s when * /
@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
private static final String HEADER_NAME = "X-Forwarded-Prefix";
private static final String URI = "/v2/api-docs";
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if(! StringUtils.endsWithIgnoreCase(path,URI )) {return chain.filter(exchange);
}
String basePath = path.substring(0, path.lastIndexOf(URI));
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
returnchain.filter(newExchange); }; }}Copy the code
2.3.2 My project configuration
package com.holland.gateway.swagger;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
public class SwaggerRouteFilter {
private static final String HEADER_NAME = "X-Forwarded-Prefix";
private static final String URI = "/v2/api-docs";
public static WebFilter getWebFilter(a) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if(! StringUtils.endsWithIgnoreCase(path, URI)) {return chain.filter(exchange);
}
String basePath = path.substring(0, path.lastIndexOf(URI));
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
returnchain.filter(newExchange); }; }}Copy the code
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.addFilterAfter(SwaggerRouteFilter.getWebFilter(), SecurityWebFiltersOrder.FIRST)
.addFilterAfter(tokenFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
.addFilterBefore(logFilter(), SecurityWebFiltersOrder.LAST)
.csrf(ServerHttpSecurity.CsrfSpec::disable);
return http.build();
}
Copy the code
2.4. Key points: Only by obtaining information of other services can the Swagger of other services be integrated
2.4.1. Service information obtained from the configuration file
Configuration files such as the following can integrate filesystem’s Swagger information
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: filesystem
uri: http://localhost:8763
predicates:
- Path=/filesystem/**
filters:
- StripPrefix=1
Copy the code
The Config writing
package com.holland.gateway.swagger;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Component
@Primary
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
/** * This method can only obtain the routing rules in the configuration file */
@Resource
private RouteLocator routeLocator;
@Resource
private GatewayProperties gatewayProperties;
@Override
public List<SwaggerResource> get(a) {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = newArrayList<>(); routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); 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) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
returnswaggerResource; }}Copy the code
2.4.2. Service information obtained through Eureka
Just modify the public List
get() method
@Override
public List<SwaggerResource> get(a) {
List<SwaggerResource> resources = new ArrayList<>();
discoveryClient.getServices()
.stream()
// Exclude modules that don't need Swagger and their own modules
.filter(s -> !"eureka".equals(s) && !"admin".equals(s) && !"gateway".equals(s))
.forEach(appName -> resources.add(swaggerResource(appName, appName + "/swagger/v2/api-docs")));
return resources;
}
Copy the code
2.4.3. Integrate Gateway itself into Swagger
You only need to change a little bit of code
List<SwaggerResource> resources = new ArrayList<>() {{
add(swaggerResource("gateway"."/v2/api-docs"));
}};
Copy the code
2.5. Precautions
API /swagger-resources is called when accessing /doc.html. The underlying invokes SwaggerResourceConfig. Get () method.
So if you get an error here, be sure to consider the situation:
- api
/swagger-resources
The obtained array cannot be empty; That is, information about other services must be obtained. - At least the other services
knife4j-micro-spring-boot-starter
To ensure that the interface is called/v2/api-docs
Not 404.
/v2/api-docs 404 = /v2/api-docs 404
3. Configure other services
Swagger core configuration Docket
package com.holland.gateway.swagger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
@Value("${spring.application.name}")
private String name;
@Bean
public Docket defaultApi2(a) {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(groupApiInfo(name))
.select()
// Specify the Controller scan package path
.apis(RequestHandlerSelectors.basePackage("com.holland." + name + ".controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo groupApiInfo(String name) {
return new ApiInfoBuilder()
.title("Back-end interface Documentation -" + name)
.description("
description
")
.termsOfServiceUrl("N")
.contact(new Contact("HollanZang"."https://juejin.cn/user/352263461681214"."[email protected]"))
.version("1.0") .build(); }}Copy the code
3.2. Configure a Swagger forwarding Interface
The reason for adding a forwarding interface here is that the access path of the /doc. HTML page is not correct when debugging the interface of other services. But no more official solution was found, so this approach was adopted.
If there is a good way to achieve the hope can comment message exchange ~
package com.holland.filesystem.swagger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.swagger2.web.Swagger2ControllerWebFlux;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/** * Swagger forward controller * configures the URL prefix */ for KNIfe4J
@Controller
public class SwaggerForwardController {
@Resource
private Swagger2ControllerWebFlux swagger2ControllerWebFlux;
@GetMapping("/swagger/v2/api-docs")
publicResponseEntity<? > forward(@RequestParam(value = "group", required = false) String swaggerGroup,
ServerHttpRequest request) {
final ResponseEntity<Json> documentation = swagger2ControllerWebFlux.getDocumentation(swaggerGroup, request);
final Map<String, Object> map = JSON.parseObject(documentation.getBody().value(), Map.class);
map.computeIfPresent("basePath", (k, pathsObj) -> "/filesystem");
map.computeIfPresent("paths", (k, pathsObj) -> {
final Map<String, JSONObject> paths = ((JSONObject) pathsObj).toJavaObject(Map.class);
final Map<String, JSONObject> res = new HashMap<>(paths.size());
paths.forEach((k1, v) -> res.put("/filesystem" + k1, v));
return res;
});
returnResponseEntity.ok(map); }}Copy the code
4. Access the interface document
And you’re done!