最简单有效的方式是使用 input[type="number"] 并配合 min 和 max 属性,辅以 JavaScript 实时清理与后端严格校验;它原生阻止非法字符、触发浏览器提示,但需注意 iOS Safari 兼容性及前后端双重验证。

用 input[type="number"] 限制基础输入范围最简单
浏览器原生支持的数字输入框能直接阻止用户输入字母或符号,还能通过 min 和 max 控制合理区间。比如年龄一般设为 min="1" 到 max="120",用户点上下箭头或手动输超界数字时,表单会自动标红并提示“请输入介于 1 和 120 之间的值”(具体文案因浏览器而异)。
注意:这层校验只在提交时触发,且可被绕过(例如直接修改 HTML 或用脚本赋值),所以仅作前端友好提示,不能替代后端验证。
-
type="number"不允许输入空格、中文、字母,但允许输入"-"和"e"(如-5、1e2),需额外处理 - 移动端会弹出数字键盘,体验更好;但 iOS Safari 对
min/max支持不稳定,建议加 JS 补充校验 - 别用
type="text"+ 正则模拟数字输入——既增加复杂度,又破坏可访问性
用 pattern 和 title 做自定义格式校验
如果必须用 type="text"(比如要兼容老旧系统或统一 UI 样式),可用 pattern 强制只接受 1–3 位数字,并用 title 定义报错提示:
<input type="text" name="age" pattern="\d{1,3}" title="请输入1到120之间的整数">
但要注意:pattern 是正则匹配整个值,\d{1,3} 会放行 "007" 或 "123",无法排除 "0" 或超上限值。所以它适合做“格式初筛”,而非逻辑校验。
立即学习“前端免费学习笔记(深入)”;
-
pattern在 Chrome/Firefox 中触发表单checkValidity(),但 Safari 对它的错误提示支持较弱 - 不要写
pattern="[1-9][0-9]?"试图匹配 1–99——它不匹配"100",且无法覆盖 100–120 - 若用
pattern,务必同步在 JS 中复用相同逻辑,避免前后端理解不一致
用 JavaScript 的 addEventListener("input") 实时清理非法字符
用户粘贴、拖拽或调试器赋值都可能绕过 type="number" 的限制。监听 input 事件,实时过滤非数字字符,并限制数值范围,是最稳妥的前端防护:
const ageInput = document.querySelector('input[name="age"]');
ageInput.addEventListener('input', () => {
let value = ageInput.value.replace(/\D/g, ''); // 删除所有非数字
if (value !== '' && (value < 1 || value > 120)) {
value = value < 1 ? '1' : '120';
}
if (ageInput.value !== value) {
ageInput.value = value;
}
});
这段代码会在每次输入后立即修正,但要注意:它会清空用户正在输入的中间状态(比如想输 115 却被截成 11),所以更推荐在 blur 或提交时做最终校验,而非过度干预输入过程。
- 别用
keypress——它不捕获粘贴、右键粘贴、IME 输入等操作 - 对空字符串、全零(
"000")、前导零("05")要单独判断,避免误判 - 如果表单有多个字段,把校验逻辑封装成函数,避免重复代码
后端必须校验 age 字段的类型和范围
任何前端校验都只是用户体验优化。攻击者可以禁用 JS、改写请求、用 curl 直接发包——你收到的 age 可能是字符串 "abc"、负数 -42、极大整数 999999999,甚至数组 [1,2,3](取决于后端框架解析方式)。
后端要做三件事:类型转换、范围检查、非空判断。以 Node.js + Express 为例:
const age = parseInt(req.body.age, 10);
if (isNaN(age) || age < 1 || age > 120) {
return res.status(400).json({ error: 'age must be an integer between 1 and 120' });
}
Python Flask、PHP Laravel 等也类似:先转成整型,再比对,失败就返回明确错误,不尝试“智能纠正”。
- 别信任
req.body.age >= 1这种比较——字符串"10"会大于"2"(字典序) - 数据库字段应定义为
TINYINT或SMALLINT,而非VARCHAR,从存储层加固 - 如果业务允许“未知年龄”,用
NULL表示,而不是传"0"或"--"
min/max 的静默失效,以及后端没做 parseInt 就直接比较字符串。这两处一漏,年龄校验就形同虚设。



















