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

使用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 = {
    '<': '<',
    '>': '>',
    '&': '&',
    '"': '"',
    "'": ''',
    '/': '&#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. 正则表达式匹配目标错误

原正则/[&lt;&gt;&amp;&quot;']/g是匹配已转义的HTML字符(比如&lt;),而非用户实际输入的原始特殊字符(比如<),导致根本匹配不到需要替换的内容。

2. HTML实体映射表的键值不匹配

映射表的键应该是用户输入的原始字符,而非转义后的字符串,比如应该用<作为键,而不是&lt;。

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 = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
    '/': '&#x2F;'
  };
  // 空值判断,避免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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 02:18:12