mcpField
N
netlify-forms

netlify-forms

github.com/netlify开发473.1k 次安装6 天前更新
代码仓库

关于 netlify-forms

netlify-forms 是帮助开发者在 Netlify 上配置和排查表单的技能。它说明构建时表单检测、静态骨架文件、AJAX 提交、反垃圾机制及提交数据读取方式。

主要功能

  1. 01通过 HTML 属性启用表单检测
  2. 02为动态表单准备静态骨架
  3. 03使用 URL 编码提交表单
  4. 04配置上传与成功页面
  5. 05使用蜜罐和 reCAPTCHA 防垃圾
  6. 06通过 API 分页读取提交记录

适用场景

  1. 01在静态站点收集表单提交
  2. 02让 SSR 或 JavaScript 表单完成注册与提交
  3. 03排查未显示的表单提交
  4. 04读取并检查垃圾提交记录

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/netlify/context-and-tools --skill netlify-forms

安装后在 Claude Code 中输入/netlify-forms确认已加载。

详细介绍

Netlify 表单

在 <form> 标签上添加 data-netlify="true"(或等效的裸 netlify 属性)以标记表单供检测。Netlify 会在部署时通过解析最终构建的 HTML检测表单——不会调用运行时 API,也不需要后端代码。客户端渲染、JS 渲染或 SSR 表单不会出现在构建后的 HTML 中,因此无法单独检测到;它们需要一个静态骨架文件(见下文)。

前置条件:必须先在 Netlify UI 中启用一次表单检测(Forms > Enable form detection)。设置会在下一次部署时生效。

静态 HTML 表单

<form name="contact" method="POST" data-netlify="true">
  <p><label>Your Name: <input type="text" name="name" /></label></p>
  <p><label>Your Email: <input type="email" name="email" /></label></p>
  <p><label>Message: <textarea name="message"></textarea></label></p>
  <p><button type="submit">Send</button></p>
</form>
  • name 用于设置 UI 中的表单名称,且每个站点都必须唯一。
  • 部署时,Netlify 会移除 data-netlify/netlify 属性,并注入 <input type="hidden" name="form-name" value="contact" />。
  • 添加 <input name="email">,以便将通知邮件的 Reply-to 设置为提交者的地址。

JS 渲染 / SSR / 框架表单(Next.js、Nuxt、SvelteKit、Astro、Gatsby)

需要满足以下两项:

1. 静态骨架文件 public/__forms.html ——为每个表单添加一份隐藏副本,包含 data-netlify="true"、一个隐藏的 form-name 输入框,以及组件提交的所有字段,字段名称必须完全一致(Netlify 会根据已注册的表单验证字段名称)。缺少此文件时,提交会静默失败。


<form name="pizzaOrder" data-netlify="true" hidden>
  <input type="hidden" name="form-name" value="pizzaOrder" />
  <input name="order" type="text" />
</form>

2. 渲染后的表单中必须包含匹配的隐藏 form-name 输入框:

<form name="pizzaOrder" method="post" data-netlify="true" onSubmit={handleSubmit}>
  <input type="hidden" name="form-name" value="pizzaOrder" />
  <input name="order" type="text" onChange={handleChange} />
  <input type="submit" />
</form>

**⚠️ SSR POST 目标:**在 SSR 应用中,fetch("/") 会被 SSR 全匹配函数拦截,无法到达表单处理流程。请将 POST 发送到静态骨架文件本身 /__forms.html,而不是 / 或其他任意路径。

**⚠️ Astro 按需路由:**带有 export const prerender = false 或 output: "server" 的路由不会在构建时扫描,因此其表单永远不会注册。请将表单放在预渲染页面上,或使用静态骨架文件。

**Next.js Runtime v5(Next.js 13.5+):**将表单定义提取到静态骨架文件,并通过 AJAX 提交,而不是进行整页导航。请参阅 https://docs.netlify.com/build/frameworks/framework-setup-guides/nextjs/overview#v5-breaking-changes

AJAX 提交

const handleSubmit = event => {
  event.preventDefault();
  const formData = new FormData(event.target);
  fetch("/__forms.html", {   // static sites may POST to "/"; SSR must target the skeleton file
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams(formData).toString()
  })
    .then(() => alert("Thank you for your submission"))  // or navigate("/thank-you")
    .catch(error => alert(error));
};
document.querySelector("form").addEventListener("submit", handleSubmit);
  • 请求正文必须使用 URL 编码。不支持 JSON。
  • 如果渲染后的表单没有隐藏的 form-name 输入框,则必须在 POST 请求正文中包含 form-name 字段。
  • honeypot 字段名称和 g-recaptcha-response(如果使用)必须包含在请求正文中——使用 FormData() 时会自动包含。

文件上传

添加 type="file";也可以选择在 <form> 上添加 enctype="multipart/form-data"。对于 AJAX 文件上传,不要设置 Content-Type 标头——让浏览器自行设置(包含 multipart 边界)。

document.forms.fileForm.addEventListener("submit", event => {
  event.preventDefault();
  fetch("/", { body: new FormData(event.target), method: "POST" })  // no headers
    .then(() => { /* success */ });
});

限制:每个字段只能上传一个文件(多个文件请使用多个字段)· 请求大小上限为 8 MB · 上传超时为 30 秒 · 删除表单后,已上传文件仍可通过其直接 URL 访问 24 小时。PII 上传需要额外的安全措施(Very Good Security 集成)。

自定义成功页面

添加一个相对于站点根目录、以 / 开头的 action 路径。请使用不带扩展名的路径——Netlify 会在 /thank-you 提供 thank-you.html;带 .html 的路径会返回 404。

<form name="contact" action="/thank-you" method="POST" data-netlify="true"></form>

自定义成功提示只能通过 AJAX 实现(用你自己的逻辑替换重定向)。

垃圾信息防护

所有提交内容都会经过 Akismet 过滤。通过 → 已验证的提交;被标记 → 垃圾信息提交。蜜罐/reCAPTCHA 验证失败的提交会被拒绝,且不会出现在任何列表中。

**蜜罐:**在 <form> 中添加 netlify-honeypot="bot-field",并包含一个该名称的 CSS 隐藏字段。任何输入值都会导致提交被静默拒绝。

<form name="contact" method="POST" netlify-honeypot="bot-field" data-netlify="true">
  <p class="hidden"><label>Don’t fill this out: <input name="bot-field" /></label></p>
  
</form>

**Netlify reCAPTCHA 2:**在 <form> 中添加 data-netlify-recaptcha="true",并在其呈现位置添加一个空的 <div data-netlify-recaptcha="true"></div>。每个页面只能使用一个由 Netlify 提供的验证挑战;如需多个,请使用自定义 reCAPTCHA。对于由 JS 渲染的表单,还需将该 div 添加到静态骨架文件中。

**自定义 reCAPTCHA 2:**使用你自己的 reCAPTCHA 代码片段,并在 <form> 中添加 data-netlify-recaptcha="true",此外还需设置环境变量:

  • SITE_RECAPTCHA_KEY — 站点密钥(作用范围:Builds + Runtime)
  • SITE_RECAPTCHA_SECRET — 密钥(作用范围:Runtime)

电子邮件通知和主题行

默认发件人:[email protected]。通过隐藏的 subject 输入字段或 Netlify UI(Forms > Submission notifications)设置主题 — 不能同时使用两种方式;HTML 中的值始终会覆盖 UI 设置。

<input type="hidden" name="subject" value="New lead from %{formName} (%{submissionId})" />

变量:%{formName}、%{siteName}、%{submissionId}。在 2023 年 5 月 5 日之前创建的表单,其主题带有 [Netlify] 前缀;若要移除该前缀,请为 subject 输入字段添加 data-remove-prefix 属性。

在 UI 中设置通知(电子邮件/webhook/Slack):Forms > Submission notifications > Add notification。

ChatGPT 自动化:订阅来自 ChatGPT 的表单提交事件,并据此运行自动化(例如,审核新提交并标记需要跟进的提交)。设置方法:https://docs.netlify.com/build/build-with-ai/agent-setup-guides/use-netlify-with-chatgpt/#subscribe-to-netlify-events

通过 API 读取提交内容

仅使用有文档说明的接口。请勿编造 api.netlify.com 端点,也不要从本地 CLI 配置文件中读取令牌。参考:https://open-api.netlify.com/#tag/submission/operation/listFormSubmissions

  • 使用 Link 标头翻阅结果 — 只读取第一个响应的代码会静默丢弃其余结果。
  • listFormSubmissions 会返回旧字段或已移除字段的数据,这些字段已不再显示在 UI 中。
  • 使用 ?state=spam 查询垃圾信息。

提交摘要(字段顺序很重要)

UI 摘要根据字段类型生成,而非字段名称:

  • 标题:第一个非隐藏的文本 <input>,且其内容不像电子邮件(type="email",或名称匹配 email/mail/from/twitter/sender);若没有,则回退到名为 title 或 subject 的字段。
  • 正文:第一个 <textarea>。

HTML 中的字段顺序会影响摘要中显示的内容。

调试缺失的提交

  • 首先怀疑 Akismet 误判。合法提交缺失通常是因为被标记为垃圾信息 — 请检查垃圾信息列表(或 API ?state=spam),并将其标记为已验证。不要一开始就构建自定义恢复函数或禁用垃圾信息过滤。
  • 测试提交被标记为垃圾信息:请使用真实电子邮件地址(不要使用 [email protected]),填写完整句子,并避免从同一个 IP 频繁提交。
  • 完全没有提交:确认已启用表单检测(Forms > Form detection),然后重新部署。
  • SSR/JS 表单静默失败:确认静态骨架文件存在,且字段名称完全一致;同时确认 AJAX 指向骨架文件,而不是 /。
  • 缺少旧字段数据:UI 仅显示最近部署的表单版本中的字段。请将旧字段标记为 hidden,而不是移除它们,以便继续显示;旧数据仍可通过 listFormSubmissions 获取。
  • 仍无法解决:参阅 Netlify Support Guide,了解如何调试表单 — https://answers.netlify.com/t/common-issue-how-to-debug-your-form/92

限制

  • 删除表单是永久操作:后续提交会返回 404,过去的提交将无法访问。请先导出 CSV。
  • 提交的代码会经过清理(<script> → 转义实体)。
  • 对于 PII,请定期导出并删除数据。
  • 数据存储在 Netlify 的数据库中,只能通过 UI/API/CSV 访问。

Netlify 表单约定

这些是组织惯例和实践中总结的防护措施,并非文档事实——ctx-gen 会将其合并到渲染后的技能中,且不会生成这些内容。 摘自之前手写的 netlify-forms 技能;由技能维护者负责维护。

  1. 在 SSR 应用(Next.js、Nuxt、SvelteKit 等)中,fetch("/") 会被 SSR 全匹配函数拦截,永远不会到达 Netlify 的表单处理流程。 将 AJAX 提交 POST 到静态骨架文件本身(例如 /__forms.html),不要提交到任意路径。
  2. 只使用有文档说明的接口:不要使用虚构的端点格式调用 https://api.netlify.com/...,也不要从本地 CLI 配置文件(~/Library/Preferences/netlify/config.json)中读取令牌。
  3. 通过 API 读取提交内容时,要翻页获取结果(Link 标头);只读取第一个响应的代码会悄无声息地丢弃其余内容。
  4. 对于由 JS 渲染的表单和 SSR 表单,始终创建静态骨架文件 public/__forms.html:其中包含每个表单的隐藏副本,带有 data-netlify="true"、隐藏的 form-name 输入项,以及组件提交的每个字段——字段名称必须完全匹配(Netlify 会根据已注册的表单验证字段名称)。没有此文件,提交会悄无声息地失败。
  5. Astro 按需渲染的路由(export const prerender = false 或 output: "server" 路由)永远不会在构建时扫描,因此其中的表单永远不会注册。请将表单放在预渲染页面上,或依赖静态骨架文件。
  6. “缺失”的有效提交通常是 Akismet 误判:请检查垃圾邮件列表(或通过 API 使用 ?state=spam),并将其标记为已验证。不要把自定义恢复函数或关闭垃圾邮件过滤作为首选方案。
  7. 对于自定义成功页面,请使用不带扩展名的 action 路径(/thank-you,而不是 /thank-you.html)——Netlify 会在 /thank-you 提供 thank-you.html,而 .html 路径会返回 404。

评论

支持 Markdown