knife4j、swagger、springdoc 返回接口分组排序问题

一、直击问题

解决前后顺序对比

解决方法:

在配置文件中添加排序规则方法sortTagsAlphabetically:

package com.example.demo.config;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Comparator;
import java.util.stream.Collectors;

@OpenAPIDefinition(
        security = @SecurityRequirement(name = "Authorization")
)
@SecurityScheme(type = SecuritySchemeType.APIKEY, name = "Authorization", scheme = "Authorization", in = SecuritySchemeIn.HEADER)
@Configuration
public class OpenApiConfig {
    private String title = "SpringDoc API";
    private String description = "SpringDoc Application";
    private String version = "v0.0.1";

    @Bean
    public OpenAPI springOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title(title)
                        .description(description)
                        .version(version));
    }

    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
                .group(title)
                .pathsToMatch("/**")
                .addOpenApiCustomiser(sortTagsAlphabetically())
                .build();
    }

    public OpenApiCustomiser sortTagsAlphabetically() {
        return openApi -> openApi.setTags(openApi.getTags()
                .stream()
                .sorted(Comparator.comparing(tag -> StringUtils.stripAccents(tag.getName())))
                .collect(Collectors.toList()));
    }

}

其他方法:如果你的项目可以升级,可以参考knife4j 的官方Issues 中的解决办法https://gitee.com/xiaoym/knife4j/issues/I5Z1YP

二、回顾历史

之前项目里一直用的都是springfox+knife4j来实现自动生成在线接口文档的,基本pom坐标如下:

<!-- Swagger2 核心依赖 -->
<dependency>
    <groupId>io.springfox</groupId>
	<artifactId>springfox-swagger2</artifactId>
	<version>2.6.1</version>
</dependency>
<!-- Swagger2 ui页面 -->
<dependency>
    <groupId>io.springfox</groupId>
	<artifactId>springfox-swagger-ui</artifactId>
	<version>2.6.1</version>
</dependency>
<!--配合Swagger2 形成一个knife4j页面 -->      
<dependency>
    <groupId>com.github.xiaoymin</groupId>
	<artifactId>knife4j-spring-boot-starter</artifactId>
	<version>2.0.4</version>
</dependency>

基本可以看出来,用的是swagger2
最新有个项目改成了springdoc +knife4j-springdoc-ui 的形式。项目集成pom坐标如下:

<!--swagger-->
<dependency>
    <groupId>org.springdoc</groupId>
	<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.8</version>
</dependency>
<!--注意用的是 knife4j-springdoc-ui-->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
	<artifactId>knife4j-springdoc-ui</artifactId>
	<version>3.0.3</version>
</dependency>

关于knife4j-springdoc-ui 属于纯UI,没有之前用knife4j的一些增强功能,作者在gitee上对其进行了说明https://gitee.com/xiaoym/knife4j/issues/I4J6R7
当然如果很看好knife4j这款ui界面,可以直接集成knife4j-openapi3-jakarta-spring-boot-starter,是支持OpenAPI 3规范的,因为knife4j-springdoc-ui从做的gitee上来看,更多像是一个过渡产品

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
    <version>4.1.0</version>
</dependency>

三、简介knife4j

作者在自己的代码仓库介绍到:knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名knife4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍knife4j的前身是swagger-bootstrap-ui,为了契合微服务的架构发展,由于原来swagger-bootstrap-ui采用的是后端Java代码+前端Ui混合打包的方式,在微服务架构下显的很臃肿,因此项目正式更名为knife4j
当然,我在swagger-bootstrap-ui就开始用了,一直觉得还不错,从最初觉得swagger-ui 的页面丑爆了,到因为这个UI页面慢慢接受了swagger项目地址https://gitee.com/xiaoym/knife4j

四、swagger2和swagger3

OpenAPI 3.0.0 是 OpenAPI 规范的第一个正式版本,因为它是由 SmartBear Software 捐赠给 OpenAPI Initiative,并在2015年从 Swagger 规范重命名为 OpenAPI 规范。

OpenAPI 规范 (中文版)地址:https://openapi.apifox.cn/

swagger 是一个 api 文档维护组织,后来成为了 Open API 标准的主要定义者,现在最新的版本为17年发布的 Swagger3(Open Api3)。

swagger2于17年停止维护,现在最新的版本为 Swagger3(Open Api3)。

swagger2 遵循的是 OpenAPI 2 规范

swagger3 遵循的是 OpenAPI 3 规范

springfox 和 Springdoc

  •  SpringFox是 spring 社区维护的一个项目(非官方),一般集成swagger2 用的多
  • SpringDoc也是 spring 社区维护的一个项目(非官方),集成swagger3 用的多

SpringDoc支持swagger页面Oauth2登录(Open API3的内容),相较 SpringFox来说,它的支撑时间更长,无疑是更好的选择。但由于国内发展较慢,在国内不容易看到太多有用的文档,不过可以访问它的官网。它的使用了 swagger3(OpenAPI3),但 swagger3 并未对 swagger2 的注解做兼容,不易迁移,也因此,名气并不如 spring fox。

SpringFox SpringDoc
@Api @Tag
@Apilgnore @Parameter(hidden = truelor@Operation(hidden = true)or@Hidden
@ApilmplicitParam @Parameter
@ApilmplicitParams @Parameters
@ApiModel @Schema
@ApiModelProperty @Schema
@ApiOperation(value = "foo", notes = "bar") @Operation(summary = "foo", description ="bar")
@ApiParam @Parameter
@ApiResponse(code = 404.message = "foo") @ApiResponse(responseCode ="404", description = "foo")

五、解读Springdoc

官网地址:https://springdoc.org/

springdoc-openapi 其实就是swagger3 ,是swagger团队在将swagger 将其作品捐献给开源的啥来着,后来就改名叫这个springdoc了
SpringDoc是一款可以结合SpringBoot使用API文档生成工具,基于OpenAPI 3,而且项目维护和社区都在不断更新,不仅支持SpringMVC,而且还支持Spring WebFlux项目。
官网架构图如下,可以清晰的认知springdoc-openapi-ui 所在的层级

 如果没有用到knife4j ,单纯的springdoc-openapi 的UI页面查看接口,那就更简单了直接修改配置文件,添加排序方法,但是这个排序对于knife4j  是不起作用的,因为是个前端js排序,只对http://localhost:8080/swagger-ui/index.html生效。

对于http://localhost:8080/doc.html暂时只能在java 端排序好进行返回,后面会具体分析

springdoc:
  api-docs:
    #是否开启文档功能,默认为true,可不配置
    enabled: true
  swagger-ui:
    # 访问ip:host/api,可直接访问Swagger springdoc
    path: /api
    # API 排序
    tags-sorter: alpha
    # HTTP 方法排序
    operations-sorter: method

不重写排序返回的分组是随机的
配置swagger-ui 的tags-sorter是前台排序,访问http://localhost:8080/swagger-ui/index.html 
查看网络请求返回数据:会看到我们在yml里面配置好的排序规则

 查看排序规则,会发现返回的tags 顺序和实际页面显示的顺序并不一致,因为我们在配置文件设置的tags-sorter生效了

 来证明对于knife4j 无效,访问  http://localhost:8080/doc.html,可以发现页面排序完全和返回tags一致

 对http://localhost:8080/swagger-ui/index.html 进行F12 查看网页源代码,可知这里调用了js 的字符串排序方法

参考官方文档 https://springdoc.org 的 5.2 swagger-ui properties 可知,有以下2个配置项可供我们给 API 和 HTTP 方法排序:

  • springdoc.swagger-ui.tagsSorter 给 API 排序, 如果其值为 alpha 就表示按字母顺序排序。默认情况下(也就是不配置此项),API 的顺序由 swagger 自己决定(也就是没什么顺序);
  • springdoc.swagger-ui.operationsSorter 给 HTTP 方法排序,其值为 alpha 同样表示按字母顺序排序,值为 method 表示根据 HTTP 请求的类型(顺序如下:DELETE > GET > POST > PUT)排序。默认情况下,Controller 代码里面,你写的是什么顺序,swagger 就给你展示什么顺序。

 翻译过来就是:对每个API的标记列表应用排序。它可以是‘’alpha'(按字母数字路径排序)或函数(参见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort,了解如何编写排序函数)。每次传递都将两个标记名字符串传递给排序器。默认值是由Swagger UI确定的顺序。

后记

针对springdoc 页面上的Authorize 不生效问题

@Bean
public OpenAPI mallTinyOpenAPI() {
    // 作者信息
	final Contact contact = new Contact();
	contact.setName(company.getCompanyName());
	contact.setUrl(company.getCompanyWebsite());
	contact.setEmail(company.getCompanyEmail());

	// 鉴权组件
	final SecurityScheme securityScheme = new SecurityScheme().type(SecurityScheme.Type.APIKEY).scheme("bearer")
				.bearerFormat("JWT").in(SecurityScheme.In.HEADER).name(SpringDocConfig.TOKEN_KEY);

	final Components components = new Components().addSecuritySchemes(SpringDocConfig.TOKEN_KEY, securityScheme);
    return new OpenAPI()
				.info(new Info().title(systemConfig.getSysName() + "接口文档").description(systemConfig.getSysName())
						.version("v1.0.0").contact(contact))
				//配置这个只能对springdoc生效 http://localhost:8410/supplies/swagger-ui/index.html
				.addSecurityItem(new SecurityRequirement().addList(SpringDocConfig.TOKEN_KEY))
				.components(components)
				.externalDocs(new ExternalDocumentation().description(company.getCompanyCopyright()).url(company.getCompanyWebsite()));
}

knef4j 配置

@Bean
public GroupedOpenApi allSpringDocApi() {
		List<SecurityRequirement> securityRequirements = new ArrayList<>();
		securityRequirements.add(new SecurityRequirement().addList(SpringDocConfig.TOKEN_KEY));
		return GroupedOpenApi.builder().group("全部接口").pathsToMatch("/api/**")
				.addOperationCustomizer((operation, handlerMethod) -> {
					return operation.security(securityRequirements);
				}).addOpenApiCustomiser(sortTagsAlphabetically()).build();
}

// 增加排序功能
public OpenApiCustomiser sortTagsAlphabetically() {
	return openApi -> openApi.setTags(
				openApi.getTags().stream().sorted(Comparator.comparing(tag -> 
     StringUtils.stripAccents(tag.getName())))
						.collect(Collectors.toList()));
}

猜你喜欢

转载自blog.csdn.net/neusoft2016/article/details/130975683