Spring Boot集成SpringFox Swagger的Pageable参数问题
发表于:2025-01-23 作者:千家信息网编辑
千家信息网最后更新 2025年01月23日,Spring Boot项目中常使用springfox-swagger来生成REST API文档,使用springfox-swagger-ui进行API测试。 io.springfox sp
千家信息网最后更新 2025年01月23日Spring Boot集成SpringFox Swagger的Pageable参数问题
Spring Boot项目中常使用springfox-swagger来生成REST API文档,使用springfox-swagger-ui进行API测试。
io.springfox springfox-swagger2 2.8.0 io.springfox springfox-swagger-ui 2.8.0
REST API方法的参数含有org.springframework.data.domain.Pageable时,如未进行其它配置,Swagger根据接口Pageable的get/is方法生成了pageNumber、pageSize、offset、paged、unpaged、sort.sorted、sort.unsorted等参数,但实际上这些参数是无效的。
@ApiOperation(value = "Find airlines")@GetMapping(value = "/airlines")public Page searchAirlines(Airline airline, Pageable pageable) { return repository.findAll(org.springframework.data.domain.Example.of(airline), pageable); }
Spring Boot解析Pageable参数的过程请查看org.springframework.data.web.PageableHandlerMethodArgumentResolver的resolveArgument()方法:
@Overridepublic Pageable resolveArgument(MethodParameter methodParameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) { assertPageableUniqueness(methodParameter); Optional defaultOrFallback = getDefaultFromAnnotationOrFallback(methodParameter).toOptional(); String pageString = webRequest.getParameter(getParameterNameToUse(pageParameterName, methodParameter)); String pageSizeString = webRequest.getParameter(getParameterNameToUse(sizeParameterName, methodParameter)); Optional page = parseAndApplyBoundaries(pageString, Integer.MAX_VALUE, true); Optional pageSize = parseAndApplyBoundaries(pageSizeString, maxPageSize, false); if (!(page.isPresent() && pageSize.isPresent()) && !defaultOrFallback.isPresent()) { return Pageable.unpaged(); } int p = page.orElseGet(() -> defaultOrFallback.map(Pageable::getPageNumber).orElseThrow(IllegalStateException::new)); int ps = pageSize.orElseGet(() -> defaultOrFallback.map(Pageable::getPageSize).orElseThrow(IllegalStateException::new)); // Limit lower bound ps = ps < 1 ? defaultOrFallback.map(Pageable::getPageSize).orElseThrow(IllegalStateException::new) : ps; // Limit upper bound ps = ps > maxPageSize ? maxPageSize : ps; Sort sort = sortResolver.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); return PageRequest.of(p, ps, sort.isSorted() ? sort : defaultOrFallback.map(Pageable::getSort).orElseGet(Sort::unsorted));}
其中使用的参数名称为page、size、sort,因此通过Swagger传入的参数是无效的。解决方法有以下几种:
@ApiImplicitParams
使用@ApiImplicitParams隐式添加page、size、sort参数,使用@ApiIgnore忽略Pageable参数。缺点,每个方法都要添加,比较繁琐。
@ApiOperation(value = "Find airlines")@ApiImplicitParams({ @ApiImplicitParam(name = "page", dataType = "integer", paramType = "query", value = "Results page you want to retrieve (0..N)"), @ApiImplicitParam(name = "size", dataType = "integer", paramType = "query", value = "Number of records per page."), @ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query", value = "Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.")})@GetMapping(value = "/airlines")public Page searchAirlines(Airline airline, @ApiIgnore Pageable pageable) { return repository.findAll(org.springframework.data.domain.Example.of(airline), pageable);}
OperationBuilderPlugin
添加OperationBuilderPlugin组件,遇到Pageable时自动添加page、size、sort参数,同时也需使用@ApiIgnore忽略Pageable参数。
import com.fasterxml.classmate.ResolvedType;import com.fasterxml.classmate.TypeResolver;import com.google.common.base.Function;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import org.springframework.data.domain.Pageable;import org.springframework.stereotype.Component;import springfox.documentation.builders.ParameterBuilder;import springfox.documentation.schema.ResolvedTypes;import springfox.documentation.schema.TypeNameExtractor;import springfox.documentation.schema.ModelReference;import springfox.documentation.service.Parameter;import springfox.documentation.service.ResolvedMethodParameter;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spi.schema.contexts.ModelContext;import springfox.documentation.spi.service.OperationBuilderPlugin;import springfox.documentation.spi.service.contexts.OperationContext;import springfox.documentation.spi.service.contexts.ParameterContext;import static com.google.common.collect.Lists.newArrayList;import static springfox.documentation.spi.schema.contexts.ModelContext.inputParam;@Component@Order(Ordered.LOWEST_PRECEDENCE)public class PageableParameterReader implements OperationBuilderPlugin { private final TypeNameExtractor nameExtractor; private final TypeResolver resolver; private final ResolvedType pageableType; @Autowired public PageableParameterReader(TypeNameExtractor nameExtractor, TypeResolver resolver) { this.nameExtractor = nameExtractor; this.resolver = resolver; this.pageableType = resolver.resolve(Pageable.class); } @Override public void apply(OperationContext context) { List methodParameters = context.getParameters(); List parameters = newArrayList(); for (ResolvedMethodParameter methodParameter : methodParameters) { ResolvedType resolvedType = methodParameter.getParameterType(); if (pageableType.equals(resolvedType)) { ParameterContext parameterContext = new ParameterContext(methodParameter, new ParameterBuilder(), context.getDocumentationContext(), context.getGenericsNamingStrategy(), context); Function factory = createModelRefFactory(parameterContext); ModelReference intModel = factory.apply(resolver.resolve(Integer.TYPE)); ModelReference stringModel = factory.apply(resolver.resolve(List.class, String.class)); parameters.add(new ParameterBuilder() .parameterType("query") .name("page") .modelRef(intModel) .description("Results page you want to retrieve (0..N)").build()); parameters.add(new ParameterBuilder() .parameterType("query") .name("size") .modelRef(intModel) .description("Number of records per page").build()); parameters.add(new ParameterBuilder() .parameterType("query") .name("sort") .modelRef(stringModel) .allowMultiple(true) .description("Sorting criteria in the format: property(,asc|desc). " + "Default sort order is ascending. " + "Multiple sort criteria are supported.") .build()); context.operationBuilder().parameters(parameters); } } } @Override public boolean supports(DocumentationType delimiter) { return true; } private Function createModelRefFactory(ParameterContext context) { ModelContext modelContext = inputParam( context.getGroupName(), context.resolvedMethodParameter().getParameterType(), context.getDocumentationType(), context.getAlternateTypeProvider(), context.getGenericNamingStrategy(), context.getIgnorableParameterTypes()); return ResolvedTypes.modelRefFactory(modelContext, nameExtractor); }}
AlternateTypeRuleConvention
import com.fasterxml.classmate.TypeResolver;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.Ordered;import org.springframework.data.domain.Pageable;import springfox.documentation.builders.AlternateTypeBuilder;import springfox.documentation.builders.AlternateTypePropertyBuilder;import springfox.documentation.schema.AlternateTypeRule;import springfox.documentation.schema.AlternateTypeRuleConvention;import java.lang.reflect.Type;import java.util.List;import static com.google.common.collect.Lists.newArrayList;import static springfox.documentation.schema.AlternateTypeRules.newRule;@Configuration@SuppressWarnings("SpringJavaAutowiringInspection")public class SpringDataConfiguration { @Bean public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) { return new AlternateTypeRuleConvention() { @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } @Override public List rules() { return newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(pageableMixin()))); } }; } private Type pageableMixin() { return new AlternateTypeBuilder() .fullyQualifiedClassName(String.format("%s.generated.%s", Pageable.class.getPackage().getName(), Pageable.class.getSimpleName())) .withProperties(newArrayList(property(Integer.class, "page"), property(Integer.class, "size"), property(String.class, "sort"))) .build(); } private AlternateTypePropertyBuilder property(Class> type, String name) { return new AlternateTypePropertyBuilder() .withName(name) .withType(type) .withCanRead(true) .withCanWrite(true); }}
这是springfox文档中介绍的方法,这种方法创建了一个in-memory type,但我未找到添加说明的方法。可以自定义一个Page class:
import com.fasterxml.classmate.TypeResolver;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.Ordered;import org.springframework.data.domain.Pageable;import springfox.documentation.schema.AlternateTypeRule;import springfox.documentation.schema.AlternateTypeRuleConvention;import java.util.List;import static com.google.common.collect.Lists.newArrayList;import static springfox.documentation.schema.AlternateTypeRules.newRule;@Configuration@SuppressWarnings("SpringJavaAutowiringInspection")public class SpringDataConfiguration { @Bean public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) { return new AlternateTypeRuleConvention() { @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } @Override public List rules() { return newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class))); } }; } @ApiModel static class Page { @ApiModelProperty("Results page you want to retrieve (0..N)") private Integer page; @ApiModelProperty("Number of records per page") private Integer size; @ApiModelProperty("Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.") private List sort; public Integer getPage() { return page; } public void setPage(Integer page) { this.page = page; } public Integer getSize() { return size; } public void setSize(Integer size) { this.size = size; } public List getSort() { return sort; } public void setSort(List sort) { this.sort = sort; } }}
参考文档
Swagger IO
Swagger Github
Tools and Integrations
SpringFox
参数
方法
文档
生成
中常
繁琐
同时
名称
实际
实际上
接口
组件
缺点
过程
这是
项目
参考
测试
配置
问题
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
人社就业数据库
软件开发技术负责人招聘
网络安全保障 建立应急机制
湛江无限软件开发优化价格
奶块会开新的服务器吗
网络安全法自然信息受
dayz进服务器显示无法连接
山东平台软件开发
数据库数值求最大值
数据库2012jdbc
中国5g网络技术发展
监控服务器两个对调
世界上网络安全大赛
网络安全截获属于被动攻击的是
网络安全靠大家绘画作品
重庆服务器机柜公司虚拟主机
天津外观检测软件开发
腾讯云开发服务器有多大
厦门电脑软件开发公司
网络安全法第34条合法
华为网络安全股
普陀区手机软件开发流程
电脑cpu服务器
软件开发od是什么意思
acfun被关闭服务器
丰台区正规软件开发概况
北纬科技的互联网彩票
dns根服务器有多少个国家
ACD碳谱数据库安装
天津融信互联网科技公司干啥的