JavaWeb高级编程:手写分页标签代码,TaoToken 统一 Key 打通接口调试链路

发布时间:2026/10/9 2:49:37

JavaWeb高级编程:手写分页标签代码,TaoToken 统一 Key 打通接口调试链路 1. 为什么手写分页标签比引入现成组件更值得做做 JavaWeb 项目的人大多经历过这个阶段列表页数据一多前端就开始催分页后端返回的却是一坨List页码、总条数、上一页下一页全靠 JSP 里写if拼字符串。我见过不少同学直接抄一段网上的分页代码跑是能跑但参数名对不上、页码越界、搜索条件丢失调起来比重新写还累。分页标签的本质是把「数据切片」和「页码渲染」这两件事拆开。PageBean负责算清楚当前页有哪些数据、总共几页、有没有上一页下一页标签类负责把这些数字翻译成 HTMLJSP 只负责调用。这样后端换数据库、前端换样式都不用动分页逻辑本身。这篇要解决的具体场景是一个带关键字搜索的列表页需要支持页码跳转、上一页下一页、以及「第 N 页 / 共 M 页」的输入框跳转。同时分页接口在本地调试时经常出现参数传错、返回结构对不上的问题我会用 TaoToken 的统一 Key 把接口调试链路串起来让「写标签」和「验接口」两件事在同一个工作流里完成。适合谁看已经会写 Servlet 和 JSP、但分页还在用LIMIT手拼字符串的 JavaWeb 学习者或者想搞清楚SimpleTagSupport到底怎么用、tld文件每一行什么意思的开发者。下面从PageBean开始一步步把可复制的代码给出来。2. TaoToken 统一 Key 在接口调试链路里的前置准备写分页标签之前得先确认后端接口返回的数据结构是稳定的。很多分页 bug 不是标签写错而是接口返回的total字段名和前端预期不一致。所以我会先把分页查询接口调通再回来写标签。TaoToken 在这里的角色是统一入口你不需要为每个模型或每个调试场景单独配一套 Key一个 Key 就能覆盖模型对话、接口调试、编码辅助这些场景。对于分页接口调试来说最直接的是用它的模型对话能力来生成测试数据、检查 JSON 结构或者用 Coding Plan 在写标签类时做代码补全。前置准备分三步。第一步拿到 Key。访问https://taotoken.net/api-keys带 UTM?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi-keys在控制台创建一个 API Key。这个 Key 就是后面所有请求的凭证。第二步确认 Base URL。TaoToken 的 API 入口是https://taotoken.net/api注意这个地址不加 UTM 参数直接作为请求前缀使用。如果你用的是 OpenAI 兼容的客户端Base URL 填这个就行。第三步选模型。分页接口调试不需要太重的模型选一个响应快的即可。Model ID 在控制台的模型列表里能看到复制出来备用。这里有个容易踩的坑有人把 Base URL 写成带/v1的完整路径结果请求 404。TaoToken 的 API 地址就是https://taotoken.net/api具体路径由客户端拼接。如果你用 curl 测试完整请求是https://taotoken.net/api/chat/completions。注意Key 不要硬编码在 JSP 或前端代码里。调试阶段可以放在环境变量或本地配置文件生产环境走服务端转发。准备好这三样之后先别急着写标签。用一条最简单的请求验证 Key 是否可用确认返回结构正常再进入分页逻辑。下一步我会给出具体的配置片段和验证命令。3. 可复制的 PageBean、标签类与 tld 配置片段这一节是核心所有代码都可以直接复制到项目里改包名使用。我按「数据封装 → 标签类 → tld 声明 → JSP 调用」的顺序来。3.1 PageBean 封装分页参数PageBean的职责是持有当前页数据、总记录数、每页条数并计算出总页数、上一页、下一页。字段命名要和接口返回保持一致避免调试时对不上。package com.example.model; import java.util.List; public class PageBeanT { private int pageNo 1; // 当前页码 private int pageSize 10; // 每页条数 private int total; // 总记录数 private int pageSum; // 总页数 private ListT list; // 当前页数据 public void setTotal(int total) { this.total total; this.pageSum (total pageSize - 1) / pageSize; } public int getPageNo() { return pageNo; } public void setPageNo(int pageNo) { if (pageNo 1) pageNo 1; if (pageSum 0 pageNo pageSum) pageNo pageSum; this.pageNo pageNo; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize pageSize; } public int getTotal() { return total; } public int getPageSum() { return pageSum; } public ListT getList() { return list; } public void setList(ListT list) { this.list list; } public boolean isHasPrev() { return pageNo 1; } public boolean isHasNext() { return pageNo pageSum; } }注意setPageNo里做了边界处理页码小于 1 归 1大于总页数归总页数。这样即使前端传了非法页码后端也不会查出空数据。3.2 标签类继承 SimpleTagSupport标签类的作用是把PageBean渲染成 HTML。继承SimpleTagSupport后重写doTag()方法通过JspWriter输出。package com.example.tag; import com.example.model.PageBean; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.SimpleTagSupport; import java.io.IOException; public class PageTag extends SimpleTagSupport { private PageBean? pm; private String url; private String key; private String type; public void setPm(PageBean? pm) { this.pm pm; } public void setUrl(String url) { this.url url; } public void setKey(String key) { this.key key; } public void setType(String type) { this.type type; } Override public void doTag() throws JspException, IOException { JspWriter out getJspContext().getOut(); StringBuilder sb new StringBuilder(); sb.append(form classsever namepageForm action).append(url).append(); sb.append(input typehidden nametype value).append(type null ? : type).append(/); sb.append(input typehidden nameKeyWords value).append(key null ? : key).append(/); int pageNo pm.getPageNo(); int pageSum pm.getPageSum(); if (pageNo 1) { sb.append(a href).append(url).append(?pageNo1KeyWords).append(key) .append(type).append(type).append(首页/a); sb.append(a href).append(url).append(?pageNo).append(pageNo - 1) .append(KeyWords).append(key).append(type).append(type).append(上一页/a); } int begin Math.max(1, pageNo - 2); int end Math.min(pageSum, pageNo 2); for (int i begin; i end; i) { if (i pageNo) { sb.append(span classnowPage).append(i).append(/span); } else { sb.append(a href).append(url).append(?pageNo).append(i) .append(KeyWords).append(key).append(type).append(type) .append().append(i).append(/a); } } if (pageNo pageSum) { sb.append(a href).append(url).append(?pageNo).append(pageNo 1) .append(KeyWords).append(key).append(type).append(type).append(下一页/a); sb.append(a href).append(url).append(?pageNo).append(pageSum) .append(KeyWords).append(key).append(type).append(type).append(末页/a); } sb.append( 第input classinput namepageNo value).append(pageNo).append(/ / ) .append(pageSum).append(页 input classsubmit typesubmit valueGO//form); out.print(sb.toString()); } }这里把原来 JSP 里的out.print逻辑搬到了 Java 类里好处是可以用 IDE 调试、可以单元测试而且不用担心 JSP 编译时的转义问题。3.3 tld 文件声明标签在WEB-INF下新建mytags.tld声明标签名、类路径和属性。?xml version1.0 encodingUTF-8? taglib xmlnshttp://java.sun.com/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd version2.1 tlib-version1.0/tlib-version short-namemy/short-name urihttp://example.com/mytags/uri tag namepage/name tag-classcom.example.tag.PageTag/tag-class body-contentempty/body-content attribute namepm/name requiredtrue/required rtexprvaluetrue/rtexprvalue typecom.example.model.PageBean/type /attribute attribute nameurl/name requiredtrue/required rtexprvaluetrue/rtexprvalue /attribute attribute namekey/name requiredfalse/required rtexprvaluetrue/rtexprvalue /attribute attribute nametype/name requiredfalse/required rtexprvaluetrue/rtexprvalue /attribute /tag /taglibrtexprvalue设为true表示属性值可以用 EL 表达式比如pm${pageBean}。body-content设为empty因为标签不需要标签体。3.4 JSP 页面调用在 JSP 顶部引入 taglib然后在列表下方调用。% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixmy urihttp://example.com/mytags % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle分页列表/title/head body table c:forEach items${pageBean.list} varitem trtd${item.name}/tdtd${item.desc}/td/tr /c:forEach /table my:page pm${pageBean} urllist.do key${param.KeyWords} type${param.type}/ /body /html到这里标签部分就完整了。下一步是验证接口返回的数据能不能正确填充PageBean。4. 验证分页请求与返回结果是否跑通标签写完之后最容易出问题的地方是接口返回的 JSON 结构和PageBean字段对不上。我习惯先用 TaoToken 的模型对话能力快速生成一份测试数据确认字段名再写 Servlet。打开https://taotoken.net/api对应的对话入口带 UTM?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentchat发一条请求让模型返回一个分页结构的 JSON 示例。比如输入「返回一个包含 pageNo、pageSize、total、list 的 JSONlist 里放三条用户数据」。拿到返回后对照PageBean的字段检查total是不是数字、list是不是数组、pageNo有没有被正确解析。然后用 curl 直接请求你的分页接口确认参数传递正确curl -X GET http://localhost:8080/demo/list.do?pageNo2pageSize10KeyWordsjavatype1 \ -H Authorization: Bearer $TAOTOKEN_KEY返回结果里重点看三个地方total是否等于数据库总记录数、list长度是否等于pageSize、pageNo是否等于请求的页码。如果total对但list为空多半是 SQL 的LIMIT偏移算错了偏移量应该是(pageNo - 1) * pageSize。实测下来分页接口最常见的返回结构是这样的{ pageNo: 2, pageSize: 10, total: 35, pageSum: 4, list: [ {id: 11, name: user11}, {id: 12, name: user12} ] }如果接口返回的是这种结构Servlet 里直接new PageBean()把total和listset 进去再 set 页码就能传给 JSP。JSP 里用${pageBean.list}遍历用my:page渲染页码。验证通过后打开浏览器访问列表页点击第 2 页、上一页、下一页观察 URL 里的pageNo是否变化、列表内容是否跟着变。如果页码变了但内容没变检查 Servlet 有没有从request.getParameter(pageNo)取值并传给查询方法。5. 分页标签常见报错与排查对照这一节列几个我实际遇到过的报错以及对应的排查方向。报错一javax.servlet.jsp.JspException: com.example.tag.PageTag找不到类这是 tld 文件里tag-class的包名和实际类路径不一致。检查com.example.tag.PageTag是否和 Java 文件里的package声明完全一致包括大小写。另外确认编译后的.class文件在WEB-INF/classes/com/example/tag/下。报错二According to TLD or attribute directive, attribute pm does not accept any expressionstld 里pm属性的rtexprvalue没设为true或者设了但 JSP 里写的是my:page pmpageBean而不是pm${pageBean}。改成 EL 表达式即可。报错三local proxy failed或401 Unauthorized这是接口调试阶段的报错不是标签本身的问题。401说明 TaoToken 的 Key 无效或没带上。检查请求头里Authorization: Bearer Key是否完整Key 有没有多余空格。local proxy failed通常是本地代理配置和 Base URL 冲突确认https://taotoken.net/api没有被本地代理拦截。报错四Cannot read property choices of undefined这个报错出现在用模型对话调试接口返回时。说明返回的 JSON 里没有choices字段可能是请求体格式不对或者 Model ID 写错了。对照控制台里的模型列表确认 Model ID 拼写正确。报错五页码跳转后搜索关键字丢失检查标签类里拼接 URL 时有没有把KeyWords和type带上。上面给的PageTag代码里每个a的 href 都拼了KeyWords和type如果漏了跳页后搜索条件就没了。报错六OAuth相关错误如果你用的是需要 OAuth 的客户端确认回调地址和 Key 的权限范围匹配。TaoToken 的 API Key 方式不需要 OAuth直接用 Bearer Token 即可。排查顺序建议先看浏览器控制台的网络请求确认参数传对了再看服务端日志确认 SQL 执行了最后看 JSP 渲染结果确认标签输出了正确的 HTML。6. 把分页标签接入真实项目的几个建议标签跑通之后有几个细节值得再打磨一下。第一PageBean的pageSize不要写死在类里从配置文件或请求参数读。我一般设一个默认值 10允许前端通过pageSize参数覆盖但加一个上限比如 100防止有人传pageSize99999把数据库拖垮。第二标签类里的 URL 拼接要注意转义。如果key里包含或中文直接拼进 href 会出问题。可以用URLEncoder.encode(key, UTF-8)处理一下再拼。第三分页接口的total查询和list查询最好在同一个事务里或者至少保证两次查询之间没有数据变更。否则可能出现total是 35、但list查出来只有 34 条的情况。第四如果你用 TaoToken 的 Coding Plan 做长期开发可以把PageBean和PageTag作为模板代码存起来新项目直接复制。Coding Plan 的入口在https://taotoken.net/coding-plan带 UTM?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding-plan适合需要反复调试接口和写标签代码的场景。最后一点经验分页标签的样式不要写在 Java 类里。上面代码里的classsever、classnowPage这些应该抽到独立的 CSS 文件标签类只负责输出结构。这样换皮肤的时候不用重新编译 Java。整套流程走下来从PageBean到标签类到 tld 到 JSP再到用统一 Key 验证接口链路是完整的。你可以先把PageBean和PageTag复制到项目里改包名配好 tld然后用一条 curl 请求确认接口返回最后在浏览器里点一遍页码。跑通之后再回头优化样式和边界处理。
延伸阅读

更多相关文章

2026/10/9 2:49:37

微信小程序生产管理系统设计与实现:从选型到踩坑指南

简介:这是一套基于微信小程序的企业生产管理系统完整项目源码,主要面向计算机专业毕业设计、课程设计及小程序开发学习者。系统围绕用户、仓库、采购、销售四大模块展开,包含用户登录与权限管理、原材料和产品的出入库、库存低于阈值预警、采…

2026/10/9 2:44:37

大模型学习路线图:12步小白也能轻松入门并收藏!

本文提供一张清晰的十二步大模型学习路线图,帮助读者从入门到落地高效搭建完整知识体系。路线涵盖Python基础、Transformer原理、提示词工程、LangGraph、LangChain、RAG、Agent、多Agent协同、私有化部署、多模态技术、量化技术和模型微调。建议按顺序学习&#xf…

2026/10/9 2:44:37

2026瓷砖一线品牌有哪些?家装瓷砖品牌推荐

2025年全国陶瓷砖产量掉了17.8%,现在行业开窑率连一半都不到。大家都在抢存量,挑瓷砖早就不只看花色和单价了。新国标GB/T 45817-2025把防污、耐磨这些指标分成了3A到5A三级。现在买砖得看品牌实力、制造产能、产品性能、研发技术、市场渠道、品牌口碑和…

2026/10/9 3:39:39

重要时期安全保障服务:从战时态到闭环值守的全流程解析

简介:面向政企单位信息安全负责人、项目集成人员与方案编写者的重要时期安全保障服务技术方案。文档以重大政治经济时期的业务连续性为着眼点,完整覆盖防护准备、监控预警、应急处置与复盘改进等环节,并明确对标ISO/IEC 27001、GB/T 20984等标…

2026/10/9 3:39:39

随机森林分类建模实战:从原理到代码与调参

随机森林(Random Forest,RF)算是我个人最爱给刚接触分类建模的朋友推荐的第一选择。市面上的教程往往只教一句“调包就行”,但真到项目里,数据怎么喂、参数怎么试、结果怎么解释,处处有细节。这篇实战笔记就…

2026/10/9 3:39:39

架构自动化转换避坑指南:从规则设计到质量门禁

1. 先说结论:自动化转换工具到底能不能用这几年我前后经手过好几个架构改造项目,从老系统搬迁到新技术栈,从单体拆微服务,到统一通信协议,几乎每一次都会遇到同一个问题:要不要用自动化转换工具。我的答案已…

2026/10/9 3:39:39

LSTM诗歌生成实战:拆解自动写诗工程与训练代码

简介:自动写诗实验包聚焦AI与自然语言处理结合的应用场景,面向具备一定Python基础、想上手文本生成项目的学习者或开发者。资源共18个文件,压缩包约23.83MB,除Python源码与编译后的pyc文件外,还包含实验指导书、实验报…

2026/10/9 3:39:39

图像去噪算法从入门到实战:空间域、小波与深度学习全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 3:34:39

垃圾代码分级规范:从代码评审的争吵到技术债量化管理

代码评审会上,我见过最多次数的场景就是:有人甩出一句“这段代码写得跟垃圾一样”,然后整个讨论组就炸了。写代码的人不服气,反问“哪里垃圾了,你给我说清楚”,评审的人憋了半天,只能说“反正就…

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/8 10:03:20

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 0:04:27

毕业论文初稿完成后首次进行AIGC疑似度自查的摸底与分流策略

毕业论文初稿完成后首次进行AIGC疑似度自查的摸底与分流策略当数万字的学位论文初稿经历开题、实验、问卷与多轮文献梳理最终成形时,绝大多数研究生都会面临一道全新的形式审查关卡:AIGC 疑似度排查。在高校毕业审核流程中,盲审前的文本检测通…

2026/10/9 0:04:27

食堂节能改造源头工厂,商用厨房设备焕新方案广受好评

商用厨房作为餐饮经营、单位供餐的核心后勤阵地,其设备配置、动线规划与运维体系直接决定后厨作业效率、运营成本与合规性。从基础的灶具、制冷存储设备,到油烟净化、水处理等配套系统,每一个环节的合理性都与食品安全、能耗管控、消防安全挂…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑