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

Spring Boot对接私有API实现分页功能技术求助(前端Thymeleaf+Bootstrap)

实现思路与示例

1. 模型层:适配私有API的分页返回结构

首先根据API返回的分页格式,创建两个核心模型类:一个是分页结果封装类,用来映射API返回的整体分页数据;另一个是业务数据模型类,映射每条搜索结果的具体字段。

示例代码:

// 业务数据模型,根据你的API返回的单条数据字段调整
public class SearchResultItem {
    private String id;
    private String title;
    // 其他字段...
    // Getter & Setter
}

// 分页结果封装类,完全匹配API返回的分页结构
public class ApiPageResponse<T> {
    private int currentPage; // 当前页码
    private int totalPages; // 总页数
    private int pageSize; // 每页条数
    private long totalElements; // 总数据量
    private List<T> content; // 当前页的数据列表
    // Getter & Setter
}

2. 控制器层:处理分页参数与API调用

控制器需要接收前端传递的搜索关键词和分页参数(当前页码page,可选每页条数size),调用私有API获取分页数据,再将分页信息和数据传递给前端视图。

核心分页跳转逻辑:

  • 首页跳转:直接将页码设为1,重新调用API
  • 下一页跳转:当前页码+1,但不能超过totalPages
  • 末页跳转:直接将页码设为totalPages
  • 边界处理:如果传入页码小于1,默认设为1;如果大于总页数,默认设为总页数

示例代码:

@Controller
@RequestMapping("/search")
public class SearchController {

    @Autowired
    private RestTemplate restTemplate;

    private static final String PRIVATE_API_URL = "https://your-private-api-domain/search";
    private static final int DEFAULT_PAGE_SIZE = 10; // 默认每页条数

    @GetMapping
    public String search(@RequestParam("keyword") String keyword,
                         @RequestParam(value = "page", defaultValue = "1") int page,
                         @RequestParam(value = "size", defaultValue = "10") int size,
                         Model model) {
        // 1. 拼接API请求参数,注意匹配API的分页参数规则(比如部分API页码从0开始)
        UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(PRIVATE_API_URL)
                .queryParam("keyword", keyword)
                .queryParam("page", page - 1) // 若API用0-based页码则减1,1-based则直接传page
                .queryParam("size", size);

        // 调用私有API并解析返回结果
        ResponseEntity<ApiPageResponse<SearchResultItem>> response = restTemplate.exchange(
                uriBuilder.toUriString(),
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<ApiPageResponse<SearchResultItem>>() {}
        );

        ApiPageResponse<SearchResultItem> pageData = response.getBody();
        if (pageData == null) {
            // 处理API返回空的情况,构造空分页数据
            pageData = new ApiPageResponse<>();
            pageData.setContent(Collections.emptyList());
            pageData.setTotalPages(0);
        }

        // 2. 修正当前页码,避免无效值
        int currentPage = pageData.getCurrentPage() + 1; // 若API返回0-based页码,转成前端用的1-based
        int totalPages = pageData.getTotalPages();
        currentPage = Math.max(1, Math.min(currentPage, totalPages));

        // 3. 将分页数据传入模型
        model.addAttribute("keyword", keyword);
        model.addAttribute("currentPage", currentPage);
        model.addAttribute("totalPages", totalPages);
        model.addAttribute("pageSize", size);
        model.addAttribute("searchResults", pageData.getContent());

        return "search-result"; // 你的Thymeleaf视图名称
    }
}

3. 前端Thymeleaf + Bootstrap分页实现

利用Thymeleaf表达式渲染分页按钮,处理首页、上一页、下一页、末页的跳转逻辑,同时禁用无效状态的按钮(比如当前页是首页时,首页和上一页按钮禁用)。

示例代码(嵌入在Thymeleaf视图中):

<!-- 搜索结果列表 -->
<div class="list-group">
    <div th:each="item : ${searchResults}" class="list-group-item">
        <h5 th:text="${item.title}"></h5>
        <!-- 其他字段展示 -->
    </div>
</div>

<!-- Bootstrap分页组件 -->
<nav aria-label="Search results pagination">
    <ul class="pagination justify-content-center">
        <!-- 首页按钮 -->
        <li class="page-item" th:classappend="${currentPage == 1} ? 'disabled'">
            <a class="page-link" th:href="@{/search(keyword=${keyword}, page=1, size=${pageSize})}">首页</a>
        </li>
        <!-- 上一页按钮 -->
        <li class="page-item" th:classappend="${currentPage == 1} ? 'disabled'">
            <a class="page-link" th:href="@{/search(keyword=${keyword}, page=${currentPage - 1}, size=${pageSize})}">上一页</a>
        </li>
        <!-- 中间页码列表(显示当前页前后2页) -->
        <li class="page-item" th:each="pageNum : ${#numbers.sequence(
            Math.max(1, currentPage - 2), 
            Math.min(totalPages, currentPage + 2)
        )}" th:classappend="${pageNum == currentPage} ? 'active'">
            <a class="page-link" th:href="@{/search(keyword=${keyword}, page=${pageNum}, size=${pageSize})}" th:text="${pageNum}"></a>
        </li>
        <!-- 下一页按钮 -->
        <li class="page-item" th:classappend="${currentPage == totalPages} ? 'disabled'">
            <a class="page-link" th:href="@{/search(keyword=${keyword}, page=${currentPage + 1}, size=${pageSize})}">下一页</a>
        </li>
        <!-- 末页按钮 -->
        <li class="page-item" th:classappend="${currentPage == totalPages} ? 'disabled'">
            <a class="page-link" th:href="@{/search(keyword=${keyword}, page=${totalPages}, size=${pageSize})}">末页</a>
        </li>
    </ul>
</nav>

关键注意事项

  • 确认私有API的分页参数规则:是page从0开始还是1开始,参数名是page还是pageNum,每页条数参数名是size还是pageSize,必须和API保持一致。
  • 异常处理:调用私有API时要处理超时、返回错误码等情况,避免页面崩溃。
  • 分页参数传递:确保搜索关键词在分页跳转时始终携带,避免跳页后丢失搜索条件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 15:27:42