使用Yup与Formik做表单验证时,特殊字符转义失效求助
表单特殊字符转HTML实体失效问题排查
我使用Yup和Formik进行表单验证,目前空字段检查和最小长度验证功能正常,但尝试将表单输入的特殊字符转换为HTML实体的操作未生效,这些特殊字符仍会直接传入数据库,请帮忙排查问题。
原代码
import { useState, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { getPosts } from "./features/posts/postSlice"; import { createPost } from "./features/poststextonly/postSlice"; import {Formik, Form, Field, ErrorMessage} from "formik"; import * as Yup from "yup"; const sanitizeInput = (input) => { const regex = /[<>&"']/g; const htmlEntities = { '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', "'": '&#x27;', '/': '&#x2F', }; return input.replace(regex, (match) => htmlEntities[match]); } const schema = Yup.object().shape({ pst: Yup.string().min(10, 'Post must be at least 10 characters').required("Field cannot be empty").transform(sanitizeInput), }) const NewPostForm = () => { const dispatch = useDispatch(); const handleSubmit = (values, {setSubmitting}) => { dispatch(createPost(values)); setSubmitting(false); dispatch(getPosts()); } return ( <Formik initialValues={{pst: ''}} validationSchema={schema} onSubmit={handleSubmit}> {({isSubmitting, errors, touched}) => ( <div className="col-12 mt-1 mb-1"> <div className="form col-12 mb-0"> <Form> <div className="form-group mb-0 pb-0 "> <Field as="textarea" name="pst" id="pst" className={errors.message && touched.message ? 'error' : ''} style={{ backgroundColor: "rgba(0,0,0,.2)" }} /> <ErrorMessage name="pst" component="div" className="error-message bg-danger m-1 text-light" /> </div> <div className="form-group mt-0 col-12 m-0 p-0"> <button className="col-auto btn btn-sm rounded-1 p-1" type="submit" disabled={isSubmitting} style={{ marginLeft: "auto", marginRight: "0", }} > {isSubmitting ? "Submitting..." : "Submit"} </button> </div> </Form> </div> </div> )} </Formik> ); }; export default NewPostForm;
问题排查及修复方案
1. 正则表达式匹配目标错误
原正则/[<>&"']/g是匹配已转义的HTML字符(比如<),而非用户实际输入的原始特殊字符(比如<),导致根本匹配不到需要替换的内容。
2. HTML实体映射表的键值不匹配
映射表的键应该是用户输入的原始字符,而非转义后的字符串,比如应该用<作为键,而不是<。
3. 替换顺序问题
需要先处理&字符,否则先替换其他字符生成的&会被重复转义,导致结果错误。
修正后的代码
import { useState, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { getPosts } from "./features/posts/postSlice"; import { createPost } from "./features/poststextonly/postSlice"; import {Formik, Form, Field, ErrorMessage} from "formik"; import * as Yup from "yup"; const sanitizeInput = (input) => { // 直接匹配原始特殊字符 const regex = /[<>&"']/g; // 键为原始字符,值为对应的HTML实体,先处理&避免重复转义 const htmlEntities = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/' }; // 空值判断,避免input为undefined时报错 if (!input) return input; return input.replace(regex, (match) => htmlEntities[match]); } const schema = Yup.object().shape({ pst: Yup.string() .min(10, 'Post must be at least 10 characters') .required("Field cannot be empty") .transform(sanitizeInput), }) const NewPostForm = () => { const dispatch = useDispatch(); const handleSubmit = (values, {setSubmitting}) => { // 双重保险:在提交前再次执行 sanitize,确保数据被处理 const sanitizedValues = { ...values, pst: sanitizeInput(values.pst) }; dispatch(createPost(sanitizedValues)); setSubmitting(false); dispatch(getPosts()); } return ( <Formik initialValues={{pst: ''}} validationSchema={schema} onSubmit={handleSubmit}> {({isSubmitting, errors, touched}) => ( <div className="col-12 mt-1 mb-1"> <div className="form col-12 mb-0"> <Form> <div className="form-group mb-0 pb-0 "> <Field as="textarea" name="pst" id="pst" className={errors.pst && touched.pst ? 'error' : ''} style={{ backgroundColor: "rgba(0,0,0,.2)" }} /> <ErrorMessage name="pst" component="div" className="error-message bg-danger m-1 text-light" /> </div> <div className="form-group mt-0 col-12 m-0 p-0"> <button className="col-auto btn btn-sm rounded-1 p-1" type="submit" disabled={isSubmitting} style={{ marginLeft: "auto", marginRight: "0", }} > {isSubmitting ? "Submitting..." : "Submit"} </button> </div> </Form> </div> </div> )} </Formik> ); }; export default NewPostForm;
额外修复点
- 修正了Field组件的className判断:原代码用
errors.message && touched.message,但字段名是pst,应该改为errors.pst && touched.pst,否则错误样式不会正确触发。 - 在handleSubmit中增加了二次sanitize操作,作为双重保险,避免Yup transform在某些场景下未生效的情况。
内容的提问来源于stack exchange,提问作者SwiftDev
相关产品推荐
相关产品推荐

