如何使用react-hook-form实现数学题校验的反垃圾表单功能
核心问题
你的算术验证码校验失效,是因为对react-hook-form的规则配置理解错了:
- register配置项里的
value字段是用来给输入框设置初始值的,根本没有值相等校验的作用,你写在这个字段里的错误提示永远不会触发 - 表单reset之后没有刷新算术题,提交一次后题目不变,很容易被脚本批量提交
- number类型输入框返回的表单值默认是字符串,直接和数字类型的正确答案对比会出现类型错误,导致就算算对也可能被误判
修复方案
把不存在的value校验换成react-hook-form原生支持的validate自定义校验规则,提交成功后自动刷新新的算术题,对比答案时统一转成数字类型避免类型误差,修正后的代码如下:
import { useState, useMemo } from 'react' import { useForm } from 'react-hook-form' // 生成区间内随机整数 function generateRandomIntegerInRange(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min } function Contact_ContactForm_Form() { const [spamCheck, setSpamCheck] = useState({ num1: generateRandomIntegerInRange(0, 10), num2: generateRandomIntegerInRange(0, 10), }) const spamCheckAnswer = useMemo( () => spamCheck.num1 + spamCheck.num2, [spamCheck] ) const { register, handleSubmit, reset, formState: { errors }} = useForm() const onSubmit = (data) => { // 此处写实际业务提交逻辑 reset() // 提交成功后生成新的验证题 setSpamCheck({ num1: generateRandomIntegerInRange(0, 10), num2: generateRandomIntegerInRange(0, 10) }) } return ( <form onSubmit={handleSubmit(onSubmit)}> <input type='email' {...register('email', { required: { message: '请输入邮箱地址', value: true }, minLength: { message: "邮箱长度不符合要求", value: 4 }, pattern: { value: /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, message: '邮箱格式无效', }, })} /> <input type='number' placeholder={`${spamCheck.num1} + ${spamCheck.num2} = ?`} {...register('spamCheck', { required: { message: "请完成算术验证", value: true, }, // 自定义校验判断答案是否正确 validate: (inputVal) => Number(inputVal) === spamCheckAnswer || "计算错误,请重新输入" })} /> <input type='submit' /> {errors.email && <div>{errors.email.message}</div>} {errors.spamCheck && <div>{errors.spamCheck.message}</div>} </form> ) } export default Contact_ContactForm_Form
补充说明
- 这种纯前端算术题只能拦住最基础的垃圾脚本,防护能力很弱,生产环境建议搭配后端接口限流、提交一次性token、服务端二次校验答案的逻辑一起用,不然很容易被绕过
- 如果要提升前端验证的门槛,可以随机切换加减乘除运算,调整数字范围,不要固定用0-10的加法
内容的提问来源于stack exchange,提问作者JordyJordyJordyJordan
相关产品推荐
相关产品推荐

