赞
踩
未经允许不得转载!
如果你有现在使用 Springfox 的经历的话,可能就有对 api 进行分组以及排序等需求,而Springfox
(应当说是Swagger
更确切)默认是根据自然排序对 api 进行排序的,比如:"/aaa1"会排在"/aaa2"前面,会排在"/bbb1"前面。
当需要自定义排序规则时怎么办?
ApiOperation
注解的position
方法,但是很不幸的是该方法已经标记为过时了,现在保留这个方法只是为了兼容之前的版本,且高版本的已经一移除该功能了(包括后端和前端UI)。那有其他办法实现自定义排序吗?
position
已经过时了,之后的版本可能就移除了)position
/* * Copyright 2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author Yahuan Jin * @since 2019.08.24 */ @Api(tags = "test", description = "test") @RequestMapping("test/apiOperation") @RestController public class ApiOperationTestController { @ApiOperation(value = "Test position 2", tags = {"position-test"}, position = 2) @PostMapping(value = "aaa") public String testPositionAaa() { return "aaa"; } @ApiOperation(value = "Test position 1", tags = {"position-test"}, position = 1) @RequestMapping(value = "bbb") public String testPositionBbb() { return "bbb"; } }
position
的支持拓展springfox.documentation.spring.web.readers.operation.ApiOperationReade
类。
/* * Copyright 2015-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.google.common.base.Optional; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMethod; import springfox.documentation.OperationNameGenerator; import springfox.documentation.builders.OperationBuilder; import springfox.documentation.service.Operation; import springfox.documentation.spi.service.contexts.OperationContext; import springfox.documentation.spi.service.contexts.RequestMappingContext; import springfox.documentation.spring.web.plugins.DocumentationPluginsManager; import springfox.documentation.spring.web.readers.operation.OperationReader; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import static com.google.common.collect.Lists.newArrayList; import static java.util.Arrays.asList; /** * @author {@link springfox.documentation.spring.web.readers.operation.ApiOperationReader * the original author} * @author Yahuan Jin * @see springfox.documentation.spring.web.readers.operation.ApiOperationReader * @since 2019.08.24 */ @Component public class SwaggerSupportPositionApiOperationReader implements OperationReader { private static final Set<RequestMethod> allRequestMethods = new LinkedHashSet<>(asList(RequestMethod.values())); private final DocumentationPluginsManager pluginsManager; private final OperationNameGenerator nameGenerator; @Autowired public SwaggerSupportPositionApiOperationReader(DocumentationPluginsManager pluginsManager, OperationNameGenerator nameGenerator) { this.pluginsManager = pluginsManager; this.nameGenerator = nameGenerator; } @Override public List<Operation> read(RequestMappingContext outerContext) { List<Operation> operations = newArrayList(); Set<RequestMethod> requestMethods = outerContext.getMethodsCondition(); Set<RequestMethod> supportedMethods = supportedMethods(requestMethods); //Setup response message list Integer currentCount = 0; // Get position, then support position. NOTE: not support sorted by RequestMethod. int position = getApiOperationPosition(outerContext, 0); for (RequestMethod httpRequestMethod : supportedMethods) { OperationContext operationContext = new OperationContext( new OperationBuilder(nameGenerator), httpRequestMethod, outerContext, (position + currentCount)); Operation operation = pluginsManager.operation(operationContext); if (!operation.isHidden()) { operations.add(operation); currentCount++; } } Collections.sort(operations, outerContext.operationOrdering()); return operations; } private Set<RequestMethod> supportedMethods(final Set<RequestMethod> requestMethods) { return requestMethods == null || requestMethods.isEmpty() ? allRequestMethods : requestMethods; } private int getApiOperationPosition(final RequestMappingContext outerContext, final int defaultValue) { final Optional<ApiOperation> annotation = outerContext.findAnnotation(ApiOperation.class); return annotation.isPresent() ? annotation.get().position() : defaultValue; } }
position
进行数据排序拓展springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl
类。
/* * Copyright 2015-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.google.common.base.Optional; import com.google.common.collect.Multimap; import io.swagger.models.Operation; import io.swagger.models.Path; import io.swagger.models.Swagger; import org.springframework.stereotype.Component; import springfox.documentation.service.ApiDescription; import springfox.documentation.service.ApiListing; import springfox.documentation.service.Documentation; import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import static springfox.documentation.builders.BuilderDefaults.nullToEmptyList; /** * @author {@link springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl * the original author} * @author Yahuan Jin * @see springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl * @since 2019.08.24 */ @Component public class SwaggerOriginalSortedServiceModelToSwagger2MapperImpl extends ServiceModelToSwagger2MapperImpl { @Override public Swagger mapDocumentation(Documentation from) { final Swagger swagger = super.mapDocumentation(from); if (Objects.isNull(swagger)) { return null; } Map<String, Path> map__ = mapOriginalSortedApiListings(from.getApiListings()); if (map__ != null) { swagger.setPaths(map__); } return swagger; } private Map<String, Path> mapOriginalSortedApiListings(Multimap<String, ApiListing> apiListings) { Map<String, Path> paths = new LinkedHashMap<>(); for (ApiListing each : apiListings.values()) { for (ApiDescription api : each.getApis()) { paths.put(api.getPath(), mapOperations(api, Optional.fromNullable(paths.get(api.getPath())))); } } return paths; } private Path mapOperations(ApiDescription api, Optional<Path> existingPath) { Path path = existingPath.or(new Path()); for (springfox.documentation.service.Operation each : nullToEmptyList(api.getOperations())) { Operation operation = mapOperation(each); path.set(each.getMethod().toString().toLowerCase(), operation); } return path; } }
代码合在一起,方便查看
/* * Copyright 2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.google.common.collect.Ordering; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import springfox.documentation.OperationNameGenerator; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiDescription; import springfox.documentation.service.Operation; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.DocumentationPluginsManager; import springfox.documentation.spring.web.readers.operation.OperationReader; import springfox.documentation.swagger2.annotations.EnableSwagger2; import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper; import java.util.List; /** * @author Yahuan Jin * @since 2019.08.24 */ @EnableSwagger2 @Configuration public class Swagger2Config { @Bean public Docket v1ApiDocket() { Docket docket = new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com")) .paths(PathSelectors.any()) .build(); // Support position docket.apiDescriptionOrdering(new Ordering<ApiDescription>() { @Override public int compare(ApiDescription a, ApiDescription b) { final List<Operation> leftList = a.getOperations(); final List<Operation> rightList = b.getOperations(); return Integer.compare(leftList.get(0).getPosition(), rightList.get(0).getPosition()); } }); return docket; } @Primary @Bean(name = "default") public OperationReader operationReader( DocumentationPluginsManager pluginsManager, OperationNameGenerator nameGenerator) { return new SwaggerSupportPositionApiOperationReader(pluginsManager, nameGenerator); } @Primary @Bean(name = "serviceModelToSwagger2Mapper") public ServiceModelToSwagger2Mapper SwaggerOriginalSortedServiceModelToSwagger2MapperImpl() { return new SwaggerOriginalSortedServiceModelToSwagger2MapperImpl(); } }
略
2.7
(不包括)之后的版本需要做另外的处理由于默认的前端UI包springfox-swagger-ui
移除了排序功能,所以即使后端接口数据排过序了,前端照样会乱。
我的解决方案是使用第三方提供的前端UI包。比如 swagger-bootstrap-ui knife4j ,新的 api doc 页面可以和默认的同时使用,如果不需要springfox-swagger-ui
或者swagger-bootstrap-ui
直接去掉依赖就行了。
http://localhost:8080/swagger-ui.htm
http://localhost:8080/doc.html
maven 的配置方式,自己补上版本号
<properties> <!-- springfox-swagger: swagger api doc --> <springfox-swagger.version>2.7.0</springfox-swagger.version> <!-- knife4j: swagger ui --> <knife4j.version>2.0.8</knife4j.version> </properties> <dependencies> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${springfox.version}</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${springfox.version}</version> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-ui</artifactId> <version>${knife4j.version}</version> </dependency> </dependencies>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。