在 Astro 页面中构建 HTML 表单
按需渲染的 Astro 页面既可以显示表单,也可以处理表单。在本指南中,你将使用一个标准的 HTML 表单向服务器提交数据。你的 frontmatter 脚本将在服务器上处理这些数据,而不会向客户端发送任何 JavaScript。
先决条件
“先决条件”部分- 一个安装了服务器适配器的 Astro 项目。
方案
“指南”部分-
创建或确定一个
.astro
页面,它将包含你的表单和处理代码。例如,你可以添加一个注册页面src/pages/register.astro ------<h1>Register</h1> -
向页面添加一个带有一些输入的
<form>
标签。每个输入都应该有一个name
属性来描述该输入的值。确保包含一个
<button>
或<input type="submit">
元素来提交表单。src/pages/register.astro ------<h1>Register</h1><form><label>Username:<input type="text" name="username" /></label><label>Email:<input type="email" name="email" /></label><label>Password:<input type="password" name="password" /></label><button>Submit</button></form> -
使用验证属性来提供基本的客户端验证,即使在 JavaScript 被禁用的情况下也能工作。
在这个例子中,
required
会阻止表单提交,直到字段被填写。minlength
为输入文本设置了最小必需长度。type="email"
也引入了验证,它只接受有效的电子邮件格式。
src/pages/register.astro ------<h1>Register</h1><form><label>Username:<input type="text" name="username" required /></label><label>Email:<input type="email" name="email" required /></label><label>Password:<input type="password" name="password" required minlength="6" /></label><button>Submit</button></form>你可以使用
<script>
标签和 约束验证 API 来添加引用多个字段的自定义验证逻辑。为了更轻松地编写复杂的验证逻辑,你可以使用前端框架来构建表单,并选择一个表单库,如 React Hook Form 或 Felte。
-
表单提交将导致浏览器再次请求该页面。将表单的数据传输
method
更改为POST
,以将表单数据作为Request
主体的一部分发送,而不是作为 URL 参数发送。src/pages/register.astro ------<h1>Register</h1><form method="POST"><label>Username:<input type="text" name="username" required /></label><label>Email:<input type="email" name="email" required /></label><label>Password:<input type="password" name="password" required minlength="6" /></label><button>Submit</button></form> -
在 frontmatter 中检查
POST
方法,并使用Astro.request.formData()
访问表单数据。将其包装在try ... catch
块中,以处理POST
请求不是由表单发送且formData
无效的情况。src/pages/register.astro ---export const prerender = false; // Not needed in 'server' modeif (Astro.request.method === "POST") {try {const data = await Astro.request.formData();const name = data.get("username");const email = data.get("email");const password = data.get("password");// Do something with the data} catch (error) {if (error instanceof Error) {console.error(error.message);}}}---<h1>Register</h1><form method="POST"><label>Username:<input type="text" name="username" required /></label><label>Email:<input type="email" name="email" required /></label><label>Password:<input type="password" name="password" required minlength="6" /></label><button>Submit</button></form> -
在服务器上验证表单数据。这应该包括在客户端完成的相同验证,以防止对你的端点进行恶意提交,并支持没有表单验证的罕见旧版浏览器。
它还可以包括无法在客户端完成的验证。例如,本例检查电子邮件是否已存在于数据库中。
错误消息可以通过将它们存储在一个
errors
对象中并在模板中访问它来发送回客户端。src/pages/register.astro ---export const prerender = false; // Not needed in 'server' modeimport { isRegistered, registerUser } from "../../data/users"import { isValidEmail } from "../../utils/isValidEmail";const errors = { username: "", email: "", password: "" };if (Astro.request.method === "POST") {try {const data = await Astro.request.formData();const name = data.get("username");const email = data.get("email");const password = data.get("password");if (typeof name !== "string" || name.length < 1) {errors.username += "Please enter a username. ";}if (typeof email !== "string" || !isValidEmail(email)) {errors.email += "Email is not valid. ";} else if (await isRegistered(email)) {errors.email += "Email is already registered. ";}if (typeof password !== "string" || password.length < 6) {errors.password += "Password must be at least 6 characters. ";}const hasErrors = Object.values(errors).some(msg => msg)if (!hasErrors) {await registerUser({name, email, password});return Astro.redirect("/login");}} catch (error) {if (error instanceof Error) {console.error(error.message);}}}---<h1>Register</h1><form method="POST"><label>Username:<input type="text" name="username" /></label>{errors.username && <p>{errors.username}</p>}<label>Email:<input type="email" name="email" required /></label>{errors.email && <p>{errors.email}</p>}<label>Password:<input type="password" name="password" required minlength="6" /></label>{errors.password && <p>{errors.password}</p>}<button>Register</button></form>