knife4j使用(整合spring boot和spring cloud gateway)
一、背景前后端分离大大实现了项目的解耦合,可扩展性,以及专业人做专业事的特点。前端开发不仅仅有网页端,还有ios、安卓、小程序等,他们都可调用后端的接口。然而,传统的手写文档说明增加了后端开发的成本,同时也不利于前后端之间的沟通。与此同时,传统的浏览器直接测试法不仅不方便接口调用方法的指定,而且填写传递参数的方法耗时很大。为此swagger-ui这个能够在线生成接口说明文档及调试的框架诞生了。..
一、背景
前后端分离大大实现了项目的解耦合,可扩展性,以及专业人做专业事的特点。前端开发不仅仅有网页端,还有ios、安卓、小程序等,他们都可调用后端的接口。然而,传统的手写文档说明增加了后端开发的成本,同时也不利于前后端之间的沟通。与此同时,传统的浏览器直接测试法不仅不方便接口调用方法的指定,而且填写传递参数的方法耗时很大。为此swagger-ui这个能够在线生成接口说明文档及调试的框架诞生了。
Swagger-ui框架能够在线生成相关的文档说明,说明的内容包括请求地址、请求方法、请求参数、响应体说明等,同时还增加了对应的接口调试。大大方便了前端调用者的理解以及降低了后端调试的成本。然而,原生的swagger-ui不支持表单转为multipart表单提交,说明的耦合度还是很高的,而且不符合国人的习惯。为此knife4j这个封装了原生的swagger-ui框架诞生了。
框架knife4j提供了文档增强功能,进一步完善了文档的说明,迎合了国人的习惯。这些文档增强功能包括下面几点:
- 个性化配置:通过个性化ui配置项,可自定义UI的相关显示信息。
- 离线文档:根据标准规范,生成的在线markdown离线文档,开发者可以进行拷贝生成markdown接口文档,通过其他第三方markdown转换工具转换成html或pdf,这样也可以放弃swagger2markdown组件。这个功能大大降低了后端开发者的时间成本。
- 接口排序:自1.8.5后,ui支持了接口排序功能,例如一个注册功能主要包含了多个步骤,可以根据swagger-bootstrap-ui提供的接口排序规则实现接口的排序,step化接口操作,方便其他开发者进行接口对接。
与此同时,knife4j进一步完善了接口调试模块,添加了表单提交类型,增加选择multipart形式提交,而且把文档说明和调试进一步分离。
二、Maven引入
SpringBoot项目中引入如下的依赖:
<!-- knife4j-spring-boot -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
这个配置的相关jar包依赖如下图:
从图中可以看出knife4j封装了原生的swagger-ui,就是在原生的swagger-ui基础上增加了一些适合国人的功能。
三、文件配置
网关中yml需要增加如下配置:
spring:
cloud:
gateway:
locator:
enabled: true
routes:
- id: shop-dn-member
uri: lb://shop-dn-member
predicates:
- Path=/shop-dn-member/**
filters:
- SwaggerHeaderFilter
- StripPrefix=1
x-forwarded:
enabled: false
同时网关还需要写如下三个java文件配置:
@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);
};
}
}
@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<>();
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) {
log.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<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")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
在其他项目中应该加入如下的配置:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import(BeanValidatorPluginsConfiguration.class)
public @interface EnableBeanValidator {
}
@Configuration
@EnableKnife4j
@EnableSwagger2
@EnableBeanValidator
public class SwaggerConfiguration {
@Bean(value = "memberApi")
@Order(value = 1)
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.xf.member"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder() .title("member接口")
.description("member接口").termsOfServiceUrl("http://localhost:7070/") .contact(new Contact("fangxiaofan", "" , "4582841895@qq.com"))
.version("1.0") .build();
}
}
四、类中引用
业务相关类中如果想要显示接口在在线文档里,需要加入相关注解。这里注解同swagger相关的。
相关的引用注解如下:
@Api:用在请求的类上,表示对类的说明
tags=“说明该类的作用,可以在UI界面上看到的注解”
value=“该参数没什么意义,在UI界面上也看到,所以不需要配置”
@ApiOperation:用在请求的方法上,说明方法的用途、作用
value=“说明方法的用途、作用”
notes=“方法的备注说明”
@ApiImplicitParams:用在请求的方法上,表示一组参数说明
@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
name:参数名
value:参数的汉字说明、解释
required:参数是否必须传
paramType:参数放在哪个地方
· header --> 请求参数的获取:@RequestHeader
· query --> 请求参数的获取:@RequestParam
· path(用于restful接口)–> 请求参数的获取:@PathVariable
· body(不常用)
· form(不常用)
dataType:参数类型,默认String,其它值dataType=“Integer”
defaultValue:参数的默认值
@ApiResponses:用在请求的方法上,表示一组响应
@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
code:数字,例如400
message:信息,例如"请求参数没填好"
response:抛出异常的类
@ApiModel:用于响应类上,表示一个返回响应数据的信息
(这种一般用在post创建的时候,使用@RequestBody这样的场景,
请求参数无法使用@ApiImplicitParam注解进行描述的时候)
@ApiModelProperty:用在属性上,描述响应类的属性
五、实现效果
进入上图的界面要输入对应的url+/doc.html#/home即可进入接口调试的首页。
文档管理部分不仅仅生成的接口文档,包括了在线文档和离线文档。Swagger Models就是加了swagger注解的实体类,加载了Docket里面相关的配置。在文档配置下面就是对应模块的controller接口,每个接口既有文档说明又有调试部分,调试这里的配置比原生的swagger更加完善,多了响应头和提交参数相关的配置,便于测试文件上传部分。
与此同时,还有设定header请求头的功能,能够传递header的参数,有利于前端传递登录的相关token来进行网关拦截。设置的方法如下图所示。
六、总结
总之,knife4j大大简化了文档的生成和接口调试过程,简化了前端开发人员对接口的理解,迎合了国人的需求。但是文档里仍然有不合理的部分,这里需要我们进一步学习和改进相关的引入机制。
七、参考文献
郭艺宾:080-Spring Boot 整合 knife4j 地址:https://www.jianshu.com/p/48d9473de9c8
Knife4j快速开始:https://doc.xiaominfo.com/guide/useful.html
每特教育第六期微服务电商项目
更多推荐
所有评论(0)