You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring GET API中Offset与Limit参数不生效问题及解决咨询

问题描述

我有一个Spring应用,GET API及Repository代码如下:

API代码

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;

@GetMapping(path = {"/v1/Log"},
        produces = {"application/json"},
        consumes = {"application/json"})
public ResponseEntity<LogsResponse> getLog(final Pageable pageable) {
        final Page<Log> log = LogService.getActionLog(pageable);
        return ResponseEntity.ok(log.getContent());
}

Repository代码

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface LogDBRepository extends MongoRepository<Log, String> {
    Page<Log> findAll(final Pageable pageable);
}

测试时发现:使用请求参数/log?offset=1&limit=1时,API未跳过第一条数据且未限制返回1条结果,而是返回MongoDB中所有文档;但使用/log?page=1&size=1时API正常工作。希望实现通过offset和limit参数跳过第一条数据并仅返回1条结果。

测试代码如下:

@Test
public void givenPageRequest_whenGetLogCalled_returnSuccess() {

LogDBRepository.saveAll(Arrays.asList(Log.builder().id("id1").transactionId("tx1").status(LogStatus.PENDING).build(),
                Log.builder().id("id2").status(LogStatus.PENDING).transactionId("tx2").build()));

final HttpHeaders headers = new HttpHeaders();
        
headers.setContentType(MediaType.APPLICATION_JSON);

final HttpEntity httpEntity = new HttpEntity(headers);

ResponseEntity<LogsResponse> responseEntity = testRestTemplate.exchange("/log?offset=1&limit=1", HttpMethod.GET, httpEntity, LogsResponse.class);

Assertions.assertEquals(HttpStatus.OK.value(), responseEntity.getStatusCode().value());

}
解决方案

问题原因

Spring Data默认的Pageable参数绑定只识别page(页码)、size(每页条数)、sort这些参数,offset和limit不会自动映射到Pageable对象中。当传入offset和limit时,Pageable会使用默认值(page=0、size=20),导致Repository返回全量数据。

解决方法

方法一:手动接收参数并构建Pageable

直接在API方法中接收offset和limit参数,手动构造符合需求的Pageable实例:

@GetMapping(path = {"/v1/Log"}, produces = {"application/json"}, consumes = {"application/json"})
public ResponseEntity<LogsResponse> getLog(
        @RequestParam(value = "offset", required = false, defaultValue = "0") long offset,
        @RequestParam(value = "limit", required = false, defaultValue = "20") int limit) {
    // 构建基于偏移量的Pageable,跳过offset条数据,取limit条
    Pageable pageable = PageRequest.ofSize(limit).withOffset(offset);
    final Page<Log> logPage = LogService.getActionLog(pageable);
    return ResponseEntity.ok(logPage.getContent());
}

方法二:配置Spring MVC支持offset/limit参数绑定

通过自定义PageableHandlerMethodArgumentResolver,让Spring自动将offset和limit参数映射到Pageable:

import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        PageableHandlerMethodArgumentResolver pageableResolver = new PageableHandlerMethodArgumentResolver();
        // 绑定offset参数到Pageable的偏移量
        pageableResolver.setOffsetParameterName("offset");
        // 绑定limit参数到Pageable的每页大小
        pageableResolver.setSizeParameterName("limit");
        // 如果你的offset是从1开始计数(比如offset=1代表跳过第一条),开启下面的配置
        // pageableResolver.setOneIndexedParameters(true);
        resolvers.add(pageableResolver);
        WebMvcConfigurer.super.addArgumentResolvers(resolvers);
    }
}

配置完成后,原API方法无需修改,直接传入offset和limit参数即可生效。

方法三:Repository层新增offset/limit方法(不推荐)

在Repository中直接定义接收offset和limit的方法,绕过Pageable:

@Repository
public interface LogDBRepository extends MongoRepository<Log, String> {
    @Query("{}")
    List<Log> findAllByOffsetAndLimit(@Param("offset") int offset, @Param("limit") int limit);
}

这种方法破坏了Spring Data的分页规范,仅在特殊场景下使用,推荐优先使用前两种方法。

测试验证

修改完成后,在测试方法中添加断言验证返回结果:

@Test
public void givenPageRequest_whenGetLogCalled_returnSuccess() {
    // 初始化测试数据
    LogDBRepository.saveAll(Arrays.asList(
            Log.builder().id("id1").transactionId("tx1").status(LogStatus.PENDING).build(),
            Log.builder().id("id2").status(LogStatus.PENDING).transactionId("tx2").build()
    ));

    final HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    final HttpEntity httpEntity = new HttpEntity(headers);

    ResponseEntity<LogsResponse> responseEntity = testRestTemplate.exchange(
            "/log?offset=1&limit=1", 
            HttpMethod.GET, 
            httpEntity, 
            LogsResponse.class
    );

    Assertions.assertEquals(HttpStatus.OK.value(), responseEntity.getStatusCode().value());
    // 验证返回结果数量为1
    Assertions.assertEquals(1, responseEntity.getBody().getLogs().size());
    // 验证返回的是第二条数据
    Assertions.assertEquals("tx2", responseEntity.getBody().getLogs().get(0).getTransactionId());
}

内容的提问来源于stack exchange,提问作者GenuineRR1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 13:10:50