Swagger2 自定义排序
分享一下Spring+Swagger2在线文档自定义排序的代码。
这里参考swagger2 接口排序_swagger接口排序-CSDN博客提供的思路,并在此基础上做了优化。
1、引用pom信息
<!--swagger依赖(pojo注解)--> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.5.20</version> </dependency> <!--生成后端开发文档--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> <exclusions> <exclusion> <artifactId>jackson-annotations</artifactId> <groupId>com.fasterxml.jackson.core</groupId> </exclusion> </exclusions> </dependency> <!--第三方生成后端开发文档页面UI--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>2、未排序效果
现有3个接口模块
@Api(tags = "用户:用户相关接口") @RestController @RequestMapping("/system/user") public class UserController{ /** 列表 */ @ApiOperation(value = "列表", notes = "") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "pageSize", value = "页面大小", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "type", value = "标签类型", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "name", value = "标签名称", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "invalid", value = "状态", dataType = "int", paramType = "query"), }) @PostMapping("/list") public ReturnMap list(Integer pageNum, Integer pageSize, String type, String name, Integer invalid, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); Map<String, Object> data = new HashMap<String, Object>(); returnMap.setData(data); returnMap.success(); return returnMap; } @ApiOperation(value = "新增用户", notes = "") @ApiImplicitParams({ }) @PostMapping("/addUser") public ReturnMap addUser(@RequestBody User user, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); returnMap.success(); return returnMap; } }@Api(tags = "系统:字典相关接口") @RestController @RequestMapping("/system/dict") public class DictController{ /** 列表 */ @ApiOperation(value = "列表", notes = "") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "pageSize", value = "页面大小", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "type", value = "标签类型", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "name", value = "标签名称", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "invalid", value = "状态", dataType = "int", paramType = "query"), }) @PostMapping("/list") public ReturnMap list(Integer pageNum, Integer pageSize, String type, String name, Integer invalid, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); Map<String, Object> data = new HashMap<String, Object>(); returnMap.setData(data); returnMap.success(); return returnMap; } //更新角色 @ApiOperation(value = "新增字典", notes = "") @ApiImplicitParams({ }) @PostMapping("/addDict") public ReturnMap addDict(@RequestBody Dict dict, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); returnMap.success(); return returnMap; } }@Api(tags = "系统:字典类型相关接口") @RestController @RequestMapping("/system/dictType") public class DictTypeController{ /** 列表 */ @ApiOperation(value = "列表", notes = "") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "pageSize", value = "页面大小", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "type", value = "标签类型", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "name", value = "标签名称", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "invalid", value = "状态", dataType = "int", paramType = "query"), }) @PostMapping("/list") public ReturnMap list(Integer pageNum, Integer pageSize, String type, String name, Integer invalid, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); Map<String, Object> data = new HashMap<String, Object>(); returnMap.setData(data); returnMap.success(); return returnMap; } //新增角色 @ApiOperation(value = "新增字典类型", notes = "") @ApiImplicitParams({ }) @PostMapping("/addDictType") public ReturnMap addDictType(@RequestBody DictType dictType, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); returnMap.success(); return returnMap; } }Swagger2显示如下:
期望展示顺序如下:
3、自定义排序改造
3.1 添加配置类Swagger2CustomSort并继承ServiceModelToSwagger2MapperImpl
API模块的排序,使用tag名称排序,在名称前加上前缀0X0X_##_,即在名称就加上"0101_##_"、"0102_##_"、"0201_##_"、"0202_##_"。但为了页面展示效果,在排序后把前缀进行处理。
接口的排序,使用@ApiOperation的position实现。
import com.google.common.base.Optional; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import io.swagger.models.Operation; import io.swagger.models.Path; import io.swagger.models.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Primary; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import springfox.documentation.builders.BuilderDefaults; import springfox.documentation.service.ApiDescription; import springfox.documentation.service.ApiListing; import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl; import springfox.documentation.swagger2.mappers.VendorExtensionsMapper; import java.util.*; /** * 自定义排序 * * 经过代码跟踪分析,总结如下: * 1.API模块的排序是依据tag 的名称进行的,虽然@Api里保留有position属性,但是已经完全弃用。 * 2.接口的排序也是依据@ApiOperation里value的值进行的,position属性虽然废弃,但是仍可以进行设置取值。 * * 解决办法如下: * 1.API模块的排序,使用tag名称排序,在名称前加上前缀0X0X_##_,即在名称就加上"0101_##_"、"0102_##_"、"0201_##_"、"0202_##_"。但为了页面展示效果,在排序后把前缀进行处理; * 2.接口的排序,使用@ApiOperation的position实现。 * * @author https://blog.csdn.net/jinhf10/article/details/103684895 */ @Primary //同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下 @Component("ServiceModelToSwagger2Mapper") @Order(Ordered.HIGHEST_PRECEDENCE) public class Swagger2CustomSort extends ServiceModelToSwagger2MapperImpl { //API接口排序序号识别 private static final String API_SORT_PREFIX = "_##_"; @Autowired private VendorExtensionsMapper vendorExtensionsMapper; protected Map<String, Path> mapApiListings(Multimap<String, ApiListing> apiListings) { Map<String, Path> paths = Maps.newTreeMap(); Iterator var3 = apiListings.values().iterator(); while (var3.hasNext()) { ApiListing each = (ApiListing) var3.next(); List<ApiDescription> list = each.getApis(); Iterator var5 = each.getApis().iterator(); while (var5.hasNext()) { ApiDescription api = (ApiDescription) var5.next(); String position = api.getOperations().get(0).getPosition()+""; while(position.length() < 32){ position = "0"+position; } paths.put(position + "-" + api.getPath(), this.mapOperations(api, Optional.fromNullable(paths.get(api.getPath())))); } } Map<String, Path> paths2 = new LinkedHashMap<>(); for (String key : paths.keySet()) { paths2.put(key.substring(key.indexOf("-") + 1), paths.get(key)); } return paths2; } private Path mapOperations(ApiDescription api, Optional<Path> existingPath) { Path path = (Path) existingPath.or(new Path()); Iterator var4 = BuilderDefaults.nullToEmptyList(api.getOperations()).iterator(); while (var4.hasNext()) { springfox.documentation.service.Operation each = (springfox.documentation.service.Operation) var4.next(); Operation operation = this.mapOperation(each); path.set(each.getMethod().toString().toLowerCase(), operation); } return path; } protected Operation mapOperation(springfox.documentation.service.Operation from) { if (from == null) { return null; } else { Operation operation = new Operation(); operation.setSecurity(this.mapAuthorizations(from.getSecurityReferences())); operation.setVendorExtensions(this.vendorExtensionsMapper.mapExtensions(from.getVendorExtensions())); operation.setDescription(from.getNotes()); String position = from.getPosition()+""; while(position.length() < 32){ position = "0"+position; } operation.setOperationId(position + from.getUniqueId()); operation.setResponses(this.mapResponseMessages(from.getResponseMessages())); operation.setSchemes(this.stringSetToSchemeList(from.getProtocol())); Set<String> set = from.getTags(); if (set != null) { Iterator<String> it= set.iterator(); Set<String> rset= new HashSet<>(); while (it.hasNext()){ String tagName= it.next(); if(API_SORT_PREFIX!=null && !"".equals(API_SORT_PREFIX)){ if(tagName!=null && !"".equals(tagName) && tagName.indexOf(API_SORT_PREFIX)>=0){ rset.add(tagName.substring(tagName.indexOf(API_SORT_PREFIX) + API_SORT_PREFIX.length())); }else{ rset.add(tagName); } }else{ rset.add(tagName); } } operation.setTags(new ArrayList(rset)); } else { operation.setTags((List) null); } operation.setSummary(from.getSummary()); Set<String> set1 = from.getConsumes(); if (set1 != null) { operation.setConsumes(new ArrayList(set1)); } else { operation.setConsumes((List) null); } Set<String> set2 = from.getProduces(); if (set2 != null) { operation.setProduces(new ArrayList(set2)); } else { operation.setProduces((List) null); } operation.setParameters(this.parameterListToParameterList(from.getParameters())); if (from.getDeprecated() != null) { operation.setDeprecated(Boolean.parseBoolean(from.getDeprecated())); } return operation; } } @Override protected List<Tag> tagSetToTagList(Set<springfox.documentation.service.Tag> set) { List<Tag> tlist = super.tagSetToTagList(set); Collections.sort(tlist, new Comparator<Tag>() { @Override public int compare(Tag t1, Tag t2) { return t1.getName().compareTo(t2.getName()); } }); List<Tag> result = new LinkedList<>(); Iterator<Tag> it = tlist.iterator(); while (it.hasNext()) { Tag tag = it.next(); Tag tt = new Tag(); if(API_SORT_PREFIX!=null && !"".equals(API_SORT_PREFIX)){ if(tag.getName()!=null && !"".equals(tag.getName()) && tag.getName().indexOf(API_SORT_PREFIX)>=0){ tt.setName(tag.getName().substring(tag.getName().indexOf(API_SORT_PREFIX) + API_SORT_PREFIX.length())); }else{ tt.setName(tag.getName()); } }else{ tt.setName(tag.getName()); } tt.setDescription(tag.getDescription()); result.add(tt); } return result; } }3.2 接口Controller调整
做如下调整:
具体代码如下:
@Api(tags = "00101_##_用户:用户相关接口") @RestController @RequestMapping("/system/user") public class UserController{ /** 列表 */ @ApiOperation(value = "列表", notes = "", position = 1) @ApiImplicitParams({ @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "pageSize", value = "页面大小", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "type", value = "标签类型", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "name", value = "标签名称", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "invalid", value = "状态", dataType = "int", paramType = "query"), }) @PostMapping("/list") public ReturnMap list(Integer pageNum, Integer pageSize, String type, String name, Integer invalid, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); Map<String, Object> data = new HashMap<String, Object>(); returnMap.setData(data); returnMap.success(); return returnMap; } @ApiOperation(value = "新增用户", notes = "", position = 2) @ApiImplicitParams({ }) @PostMapping("/addUser") public ReturnMap addUser(@RequestBody User user, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); returnMap.success(); return returnMap; } }@Api(tags = "00202_##_系统:字典相关接口") @RestController @RequestMapping("/system/dict") public class DictController{ /** 列表 */ @ApiOperation(value = "列表", notes = "", position = 1) @ApiImplicitParams({ @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "pageSize", value = "页面大小", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "type", value = "标签类型", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "name", value = "标签名称", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "invalid", value = "状态", dataType = "int", paramType = "query"), }) @PostMapping("/list") public ReturnMap list(Integer pageNum, Integer pageSize, String type, String name, Integer invalid, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); Map<String, Object> data = new HashMap<String, Object>(); returnMap.setData(data); returnMap.success(); return returnMap; } //更新角色 @ApiOperation(value = "新增字典", notes = "", position = 2) @ApiImplicitParams({ }) @PostMapping("/addDict") public ReturnMap addDict(@RequestBody Dict dict, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); returnMap.success(); return returnMap; } }@Api(tags = "00201_##_系统:字典类型相关接口") @RestController @RequestMapping("/system/dictType") public class DictTypeController{ /** 列表 */ @ApiOperation(value = "列表", notes = "", position = 1) @ApiImplicitParams({ @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "pageSize", value = "页面大小", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "type", value = "标签类型", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "name", value = "标签名称", dataType = "String", paramType = "query"), @ApiImplicitParam(name = "invalid", value = "状态", dataType = "int", paramType = "query"), }) @PostMapping("/list") public ReturnMap list(Integer pageNum, Integer pageSize, String type, String name, Integer invalid, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); Map<String, Object> data = new HashMap<String, Object>(); returnMap.setData(data); returnMap.success(); return returnMap; } //新增角色 @ApiOperation(value = "新增字典类型", notes = "", position = 2) @ApiImplicitParams({ }) @PostMapping("/addDictType") public ReturnMap addDictType(@RequestBody DictType dictType, HttpServletRequest request, HttpServletResponse response){ ReturnMap returnMap = new ReturnMap(); returnMap.success(); return returnMap; } }4、自定义排序效果
重新编译运行后,Swagger2显示如下:
