前言
最近几年一直使用showdoc来进行接口文档的管理。其实,知道swagger可以很方面的在找接口的时候找到它的文档。但是,说服我的是,我需要在研发开始的时候确认接口文档,但是这个时候是还没有代码的。而swagger就无法满足这种需要。但是,由于接口文档和代码分离,其有效性其实是大打折扣的,也很难检索和阅读以及和接口对应。最近,看到了这个框架,就试试吧,把接口文档还是挪到代码里去维护吧。
引入
官网文档地址为:https://doc.xiaominfo.com/guide/
我手上的项目是springcloud的,所以就直接看springcloud怎么引入了。本文档为折腾过程的记录。
注册中心
看官方文档,注册中心是不需要任何改变的,就是单纯的erueka注册中心
接口服务
这里它直接引入了一个starter
com.github.xiaoymin
knife4j-micro-spring-boot-starter
2.0.1
版本是我在mvnrepository.com上找的,最新版本。据说这个依赖的区别是,去掉了ui的内容。
创建配置类SwaggerConfig:
@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfig {
@Bean(value = "userApi")
@Order(value = 1)
public Docket groupRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(groupApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.xiaominfo.swagger.service.user.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo groupApiInfo(){
return new ApiInfoBuilder()
.title("swagger-bootstrap-ui很棒~~~!!!")
.description("swagger-bootstrap-ui-demo RESTful APIs")
.termsOfServiceUrl("http://www.group.com/")
.contact("[email protected]")
.version("1.0")
.build();
}
}
官网的代码比这个多一个@EnableSwaggerBootstrapUi,在类上面。应该是写错了,因为在这个项目里是没有引入ui依赖的。然后,这似乎就完了
gateway
我这里使用的是spring2.0,网关用的是Gateway。这里引入的依赖就是完整的依赖了:
com.github.xiaoymin
knife4j-spring-boot-starter
2.0.1
另说,如果在gateway上没有接口的话,单纯得引入ui也是可以的:
com.github.xiaoymin
knife4j-spring-ui
2.0.1
在yml配置文件里,配置每个服务的时候多了
filters:
- SwaggerHeaderFilter
- StripPrefix=1
这样的filter的配置在routes下每个id中进行配置。
另外,它说单体应用时使用包路径进行分组的,但是微服务架构下就是使用服务来进行业务分组了。springfox-swagger提供的分组接口是swagger-resource,返回的是分组接口名称、地址等信息。在SpringCloud微服务架构下,我们需要重写该接口,主要是通过网关的注册中心动态发现所有的微服务群的那个,代码如下:
@Component
@Primary
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
private static Logger logger= LoggerFactory.getLogger(SwaggerResourceConfig.class);
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
public SwaggerResourceConfig(RouteLocator routeLocator,GatewayProperties gatewayProperties){
this.routeLocator=routeLocator;
this.gatewayProperties=gatewayProperties;
}
@Override
public List get() {
List resources = new ArrayList<>();
List routes = new ArrayList<>();
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) {
logger.info("name:{},location:{}",name,location);
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
实现相关接口,代码如下:
@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> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources/configuration/ui")
public Mono> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources")
public Mono swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
实现SwaggerHeaderFilter过滤器,下面这段代码是官网给的,我一个字都没改。
@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();
return chain.filter(newExchange);
};
}
}
验证
请求你gateway监听端口的doc.html页面即可。具体来说,我的是:
http://localhost:8770/doc.html
怎么写接口就不说了,看官方文档或者swagger2文档自己慢慢坑吧。打完收工。
备注
guava版本过低
这个问题是在我尝试启动我的接口服务时发现的。直接的提示是调用里面的某个类时,有个方法找不到,而我当前的版本是16。这经常发生在版本过低中。由于我并没有直接引入过guava包,所以我尝试在maven依赖中找到是谁给我带进来的。最后定位到springcloud的rebbin依赖。这个大版本暂时我不想动,所以就单独引入了guava版本23,启动成功。
接口会话拦截
这个其实很容易理解。我的session拦截是做在Gateway的,而knife4j想要获得接口信息,本质上也是接口请求。所以我需要将其排除在外,而其指定这个接口就是通过Gateway中添加的SwaggerHeaderFilter 来指定的,我们将其模式匹配的排除即可。