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

如何在Next.js联系表单中实现ReCaptcha并完成服务端验证?

问题解决:ReCaptcha服务端验证失败及表单防垃圾邮件优化

一、当前代码核心问题

你导入了react-google-recaptcha组件,但完全未在表单中渲染该组件,也没有将验证令牌传递给后端,这是服务端验证失效的根本原因。另外,表单提交逻辑存在疏漏:未阻止默认提交行为、未处理异步请求错误、未在验证通过前拦截提交。

二、修复步骤(以ReCaptcha v2为例)

1. 客户端代码修改

更新Contact组件,添加ReCaptcha实例引用,获取验证令牌并随表单数据一同发送:

import React, { useState, useRef } from "react";
import ReCAPTCHA from "react-google-recaptcha";

export default function Contact() {
  const [name, setName] = useState("");
  const recaptchaRef = useRef(null); // 新增ReCaptcha引用

  async function handleSubmit(e) {
    e.preventDefault(); // 阻止默认表单提交,避免页面刷新

    // 获取ReCaptcha验证令牌
    const recaptchaToken = recaptchaRef.current?.getValue();
    if (!recaptchaToken) {
      alert("请完成人机验证");
      return;
    }

    const formData = {};
    Array.from(e.currentTarget.elements).forEach((field) => {
      if (!field.name) return;
      formData[field.name] = field.value;
    });
    // 将验证令牌加入表单数据
    formData.recaptchaToken = recaptchaToken;

    try {
      const response = await fetch("/api/email", {
        method: "post",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(formData),
      });

      if (!response.ok) throw new Error("邮件发送失败");
      
      alert(`Bedankt voor het sturen van een bericht ${name}! Ik zal spoedig contact met je opnemen.`);
      // 重置表单与ReCaptcha
      e.currentTarget.reset();
      recaptchaRef.current?.reset();
    } catch (err) {
      alert(`发送失败:${err.message}`);
    }
  }

  return (
    <main className="bg-gradient-to-b from-black to-[#434343]" id="contact">
      <section>
        <div className="flex mt-5">
          <h2 className="mx-auto text-3xl font-bold">Contact</h2>
        </div>
        <div className="flex mt-5">
          <p className="mx-auto text-center text-gray-300">
            Neem gerust contact op voor vragen of andere verzoeken!
          </p>
        </div>
        <div className="grid ld:grid-cols-2 esd:grid-cols-1 sd:grid-cols-1">
          <div className="flex">
            <img
              src="/contact/mail.png"
              alt="邮件图标"
              className="w-[75%] mx-auto my-auto"
            />
          </div>
          <div className="flex mx-32 esd:mx-5 mb-10">
            <form
              method="post"
              onSubmit={handleSubmit}
              className="mx-auto my-auto space-y-5"
            >
              <input
                type="text"
                name="naam"
                value={name}
                required
                onChange={(e) => setName(e.target.value)}
                className="w-full rounded-full p-2 border bg-white text-black"
                placeholder="Naam"
              />
              <input
                type="email"
                name="email"
                required
                className="w-full rounded-full p-2 border bg-white text-black"
                placeholder="Wat is je email?"
              />
              <textarea
                placeholder="Je vraag of verzoek..."
                name="bericht"
                required
                className="w-full h-40 rounded p-2 border bg-white text-black"
                maxLength={300}
              ></textarea>
              {/* 渲染ReCaptcha组件,替换为你的Site Key */}
              <ReCAPTCHA
                ref={recaptchaRef}
                sitekey="你的ReCaptcha Site Key"
                className="mx-auto"
              />
              <input
                type="submit"
                className="bg-gradient-to-br from-orange-400 to-[#4ECDC4] p-2 rounded cursor-pointer"
              />
            </form>
          </div>
        </div>
      </section>
    </main>
  );
}

2. 服务端验证逻辑(Node.js/Express示例)

在/api/email接口中,调用Google的ReCaptcha验证接口确认令牌有效性:

const fetch = require('node-fetch');

async function validateRecaptcha(token) {
  const secretKey = "你的ReCaptcha Secret Key";
  const response = await fetch(`https://www.google.com/recaptcha/api/siteverify`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: `secret=${secretKey}&response=${token}`
  });
  const data = await response.json();
  // v3需检查score,v2仅需检查success
  return data.success && (data.score ? data.score >= 0.5 : true);
}

// 邮件发送接口
app.post('/api/email', async (req, res) => {
  try {
    const { recaptchaToken, naam, email, bericht } = req.body;

    // 优先验证ReCaptcha
    const isHuman = await validateRecaptcha(recaptchaToken);
    if (!isHuman) {
      return res.status(403).json({ error: "人机验证失败" });
    }

    // 调用SendGrid发送邮件逻辑
    // ... 你的SendGrid代码 ...

    res.status(200).json({ message: "邮件发送成功" });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

三、防垃圾邮件优化建议

  • 改用ReCaptcha v3(无交互):若不想让用户手动点复选框,可替换为v3版本,它会在后台对用户行为评分,服务端验证时判断score(建议阈值≥0.5)。
  • 隐藏字段陷阱:添加一个CSS隐藏的输入框(如display: none),正常用户不会填写,机器人会自动填充,服务端检测到该字段有值则直接拒绝请求。
  • 限制请求频率:用Redis或内存缓存记录用户IP的提交次数,短时间内多次提交直接拦截。
  • 内容关键词过滤:对提交的消息内容做关键词匹配,拦截包含垃圾信息的请求。
  • 邮箱二次验证:除了HTML5的email类型校验,服务端用正则表达式再次验证邮箱格式。

内容的提问来源于stack exchange,提问作者HighlyC

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 05:48:08