跳转到内容
搜索文档

创建 HTML 表单

最后更新 查看 MarkdownAgent 设置

在本教程中,你将使用纯 HTML 和 CSS 创建简单的 <form> 并部署到 Cloudflare Pages。在此过程中,你将了解 HTML 表单属性以及如何在 Worker 中收集提交的数据。

本教程将大量使用 Cloudflare Pages 及其 Workers 集成。请参阅快速入门指南以熟悉该平台。

概览

在 Web 上,表单是用户与 Web 文档之间常见的交互点。它们允许用户输入数据,并通常将数据提交到服务器。表单至少包含一个表单输入,可以是文本字段、下拉菜单、复选框等。

每个输入都应有名称——使用 name 属性——以便服务器收到输入值时有可识别的名称。此外,随着 HTML5 的发展,表单元素可以声明额外属性以启用自动表单验证。可用验证因输入类型而异;例如,接受电子邮件的文本输入(通过 type=email)可确保值看起来像有效电子邮件地址,数字输入(通过 type=number)仅接受整数或小数(若允许),通用文本输入可定义自定义 pattern。但是,所有输入都可以声明值是否 required

以下是定义了若干输入及其验证规则的 HTML5 表单示例:

<form method="POST" action="/api/submit">
	<input type="text" name="fullname" pattern="[A-Za-z]+" required />
	<input type="email" name="email" required />
	<input type="number" name="age" min="18" required />

	<button type="submit">Submit</button>
</form>

若 HTML5 表单定义了验证规则,用户尝试提交表单时浏览器会自动检查所有规则。若有错误,提交将被阻止,浏览器向用户显示错误消息以供修正。<form> 仅在没有未解决的验证错误时才会将数据 POST/submit 端点。整个过程是 HTML5 原生的,只需存在适当的表单和输入属性——无需 JavaScript。

表单元素还可以关联 <label> 元素,以便清晰描述每个输入。这当然有助于视觉清晰度,还能提供更无障碍的用户体验,因为 HTML 标记定义更明确。辅助技术直接受益;例如,屏幕阅读器可以播报哪个 <input> 获得焦点。点击 <label> 时,其关联的表单输入将获得焦点,从而扩大输入的激活区域。

要启用此功能,必须为每个输入创建 <label> 元素,并为每个 <input> 元素分配唯一的 id 属性值。<label> 还必须具有 for 属性,反映其输入的唯一 id 值。修改前面的代码片段应产生以下内容:

<form method="POST" action="/api/submit">
	<label for="i-fullname">Full Name</label>
	<input
		id="i-fullname"
		type="text"
		name="fullname"
		pattern="[A-Za-z]+"
		required
	/>

	<label for="i-email">Email Address</label>
	<input id="i-email" type="email" name="email" required />

	<label for="i-age">Your Age</label>
	<input id="i-age" type="number" name="age" min="18" required />

	<button type="submit">Submit</button>
</form>

当此 <form> 以有效数据提交时,其数据内容将发送到服务器。你可以通过在表单本身上声明属性来自定义数据的发送方式和位置。若未提供这些详细信息,<form> 将通过 GET 将数据发送到当前 URL 地址,这很少是期望的行为。要解决此问题,至少需要定义带有目标 URL 地址的 action 属性,但通常也建议声明 method,即使你重新声明默认的 GET 值。

默认情况下,HTML 表单以 application/x-www-form-urlencoded MIME 类型发送内容。此值将反映在 Content-Type HTTP 标头中,接收服务器必须读取它以确定如何解析数据内容。你可以通过 enctype 属性自定义 MIME 类型。例如,要接受文件(通过 type=file),必须将 enctype 更改为 multipart/form-data 值:

<form method="POST" action="/api/submit" enctype="multipart/form-data">
	<label for="i-fullname">Full Name</label>
	<input
		id="i-fullname"
		type="text"
		name="fullname"
		pattern="[A-Za-z]+"
		required
	/>

	<label for="i-email">Email Address</label>
	<input id="i-email" type="email" name="email" required />

	<label for="i-age">Your Age</label>
	<input id="i-age" type="number" name="age" min="18" required />

	<label for="i-avatar">Profile Picture</label>
	<input id="i-avatar" type="file" name="avatar" required />

	<button type="submit">Submit</button>
</form>

由于 enctype 更改,浏览器向服务器发送数据的方式也会改变。Content-Type HTTP 标头将反映新方法,HTTP 请求的正文将符合新的 MIME 类型。接收服务器必须适应新格式并调整其请求解析方法。

实时示例

本教程的其余部分将重点在 Pages 上构建 HTML 表单,包括用于接收和解析表单提交的 Worker。

设置

首先,创建新的 GitHub 仓库。然后在本地机器上创建新目录,初始化 git,并将 GitHub 位置附加为远程目标:

# create new directory
mkdir new-project
# enter new directory
cd new-project
# initialize git
git init
# attach remote
git remote add origin [email protected]:<username>/<repo>.git
# change default branch name
git branch -M main

现在可以开始在创建的 new-project 目录中工作。

标记

此示例的表单相当简单。它包含多种不同输入类型,包括用于选择多个值的复选框。表单也不包含任何验证,以便你可以看到服务器如何解释空值和/或缺失值。

此示例项目仅使用纯 HTML。你可以使用首选的 JavaScript 框架,但为简单和熟悉起见选择了原始语言——所有框架都在抽象和/或产生类似结果。

在项目目录中创建 public/index.html。所有前端资源将位于此 public 目录中,此 index.html 文件将作为网站的主页。

将以下内容复制并粘贴到 public/index.html 文件中:

<html lang="en">
	<head>
		<meta charset="utf8" />
		<title>Form Demo</title>
		<meta name="viewport" content="width=device-width,initial-scale=1" />
	</head>
	<body>
		<form method="POST" action="/api/submit">
			<div class="input">
				<label for="name">Full Name</label>
				<input id="name" name="name" type="text" />
			</div>

			<div class="input">
				<label for="email">Email Address</label>
				<input id="email" name="email" type="email" />
			</div>

			<div class="input">
				<label for="referers">How did you hear about us?</label>
				<select id="referers" name="referers">
					<option hidden disabled selected value></option>
					<option value="Facebook">Facebook</option>
					<option value="Twitter">Twitter</option>
					<option value="Google">Google</option>
					<option value="Bing">Bing</option>
					<option value="Friends">Friends</option>
				</select>
			</div>

			<div class="checklist">
				<label>What are your favorite movies?</label>
				<ul>
					<li>
						<input id="m1" type="checkbox" name="movies" value="Space Jam" />
						<label for="m1">Space Jam</label>
					</li>
					<li>
						<input
							id="m2"
							type="checkbox"
							name="movies"
							value="Little Rascals"
						/>
						<label for="m2">Little Rascals</label>
					</li>
					<li>
						<input id="m3" type="checkbox" name="movies" value="Frozen" />
						<label for="m3">Frozen</label>
					</li>
					<li>
						<input id="m4" type="checkbox" name="movies" value="Home Alone" />
						<label for="m4">Home Alone</label>
					</li>
				</ul>
			</div>

			<button type="submit">Submit</button>
		</form>
	</body>
</html>

此 HTML 文档将包含一个表单,有几个字段供用户填写。由于表单内没有验证规则,所有字段都是可选的,用户可以提交空表单。对于此示例,这是预期行为。

Worker

HTML 表单已完成并准备部署。用户提交此表单时,所有数据将通过 POST 请求发送到 /api/submit URL。这是由于表单的 methodaction 属性。但是,目前 /api/submit 地址没有请求处理程序。现在你将创建它。

Cloudflare Pages 提供 Functions 功能,允许你定义和部署 Worker 以实现动态行为。

Functions 与 functions 目录关联,并根据 functions 文件结构方便地构建 URL 请求处理程序。例如,functions/about.js 文件将映射到 /about URL,functions/hello/[name].js 将处理 /hello/:name URL 模式,其中 :name 是任何匹配的 URL 段。有关更多信息,请参阅 Functions 路由 文档。

要为 /api/submit 定义处理程序,必须创建 functions/api/submit.js 文件。这意味着 functionspublic 目录应为同级,总项目结构类似于:

├── functions
│   └── api
│       └── submit.js
└── public
    └── index.html

<form> 将发送 POST 请求,这意味着 functions/api/submit.js 文件需要导出 onRequestPost 处理程序:

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
	// TODO: Handle the form submission
}

context 参数是包含若干可能感兴趣值的对象。对于此示例,你只需要 Request 对象,可通过 context.request 键访问。

如前所述,<form> 提交时默认为 application/x-www-form-urlencoded MIME 类型。对于更高级的场景,需要 enctype="multipart/form-data" 属性。幸运的是,两种 MIME 类型都可以解析并视为 FormData。这意味着使用 Workers——包括 Pages Functions——你可以使用原生 Request.formData 解析器。

为说明目的,示例应用的表单处理程序将回复它收到的所有值。处理程序也必须始终返回 Response

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
	try {
		let input = await context.request.formData();
		let pretty = JSON.stringify([...input], null, 2);
		return new Response(pretty, {
			headers: {
				"Content-Type": "application/json;charset=utf-8",
			},
		});
	} catch (err) {
		return new Response("Error parsing JSON content", { status: 400 });
	}
}

有了此处理程序,示例现已完全可用。收到提交时,Worker 将回复 FormData 键值对的 JSON 列表。

但是,若要以 JSON 对象而非键值对(Array of Arrays)回复,则必须手动完成。最近,JavaScript 添加了 Object.fromEntries 实用工具。这在某些情况下效果很好;但是,示例 <form> 包含允许多个值的 movies 清单。若使用 Object.fromEntries,生成的对象将只保留一个 movies 值,丢弃其余值。为避免此问题,必须编写自己的 FormDataObject 实用工具:

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
	try {
		let input = await context.request.formData();

		// Convert FormData to JSON
		// NOTE: Allows multiple values per key
		let output = {};
		for (let [key, value] of input) {
			let tmp = output[key];
			if (tmp === undefined) {
				output[key] = value;
			} else {
				output[key] = [].concat(tmp, value);
			}
		}

		let pretty = JSON.stringify(output, null, 2);
		return new Response(pretty, {
			headers: {
				"Content-Type": "application/json;charset=utf-8",
			},
		});
	} catch (err) {
		return new Response("Error parsing JSON content", { status: 400 });
	}
}

最终代码片段(上方)允许 Worker 保留所有值,返回准确表示 <form> 提交的 JSON 响应。

部署

现在可以部署项目。

若尚未完成,请在 git 中保存进度,然后将提交推送到 GitHub 仓库:

# Add all files
git add -A
# Commit w/ message
git commit -m "working example"
# Push commit(s) to remote
git push -u origin main

你的工作现在位于 GitHub 仓库中,这意味着 Pages 也可以访问它。

若这是你的第一个 Cloudflare Pages 项目,请参阅快速入门指南获取完整演练。选择适当的 GitHub 仓库后,必须使用以下构建设置配置项目:

  • Project name(项目名称) – 你的选择
  • Production branchmain
  • Framework preset(框架预设) – None
  • Build command(构建命令) – None / Empty
  • Build output directory(构建输出目录)public

点击 Save and Deploy(保存并部署) 按钮后,Pages 项目将开始首次部署。成功后,你将获得唯一的 *.pages.dev 子域名和实时演示链接。

在本教程中,你使用 Cloudflare Pages 及其 Workers 集成构建并部署了网站及其后端逻辑。你创建了一个带有表单的静态 HTML 文档,该表单与 Worker 处理程序通信以解析提交请求。

若要查看此应用的完整源代码,可在 GitHub 上找到。

相关资源

这篇文档对您有帮助吗?