Spring Boot集成SpringFox Swagger的Pageable参数问题
发表于:2025-02-24 作者:千家信息网编辑
千家信息网最后更新 2025年02月24日,Spring Boot项目中常使用springfox-swagger来生成REST API文档,使用springfox-swagger-ui进行API测试。 io.springfox sp
千家信息网最后更新 2025年02月24日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安全错误
数据库的锁怎样保障安全
清华同方服务器bmc管理
电子邮件无法安全连接到服务器
T1用友财务数据库端口
pytho数据库连接查询
云平台软件开发平台
微信服务器保留几年的转账记录
安卓模拟器20开服务器
违法网络安全法30
黄岛招聘数据库技术
nas储存服务器怎么选
美颜特效制作软件开发
网络安全工程师考试用书
淘宝用户数据库表格
南岸区网络安全应急演练
美信网络技术有限公司诈骗
服务器网络安全组配置
西安aqq软件开发公司
河南省高校网络安全知识竞答答案
计算机网络技术职称评定
南京科远dcs数据库
通信与网络技术专业英语
ps3重新构建数据库准备中
云小站云数据库
中国知网和维普数据库哪个好
学网络技术有出路吗
怎样管理服务器端口
李存龙中国书画人才数据库
服务器power on
数据库按字段归档
汕头卫星软件开发批发价格