Skip to main content
基本 Svelte
介绍
反应性
属性
逻辑
事件
绑定
类和样式
动作
转换
高级 Svelte
高级反应性
重用内容
运动
高级绑定
高级转换
上下文 API
特殊元素
<script module>
后续步骤
基本 SvelteKit
介绍
路由
加载数据
标题和 cookie
共享模块
表单
API 路由
$app/state
错误和重定向
高级 SvelteKit
钩子
页面选项
链接选项
高级路由
高级加载
环境变量
结论

用户是一群淘气鬼,如果有机会,他们会提交各种无意义的数据。为了防止它们造成混乱,验证表单数据非常重要。

¥Users are a mischievous bunch, who will submit all kinds of nonsensical data if given the chance. To prevent them from causing chaos, it’s important to validate form data.

第一道防线是浏览器的 内置表单验证,这使得将 <input> 标记为必需变得容易:

¥The first line of defense is the browser’s built-in form validation, which makes it easy to, for example, mark an <input> as required:

src/routes/+page
<form method="POST" action="?/create">
	<label>
		add a todo
		<input
			name="description"
			autocomplete="off"
			required
		/>
	</label>
</form>

尝试在 <input> 为空时按 Enter。

¥Try hitting Enter while the <input> is empty.

这种验证很有帮助,但还不够。某些验证规则(例如唯一性)无法使用 <input> 属性来表达,无论如何,如果用户是精英黑客,他们可能会简单地使用浏览器的 devtools 删除属性。为了防止这些恶作剧,你应该始终使用服务器端验证。

¥This kind of validation is helpful, but insufficient. Some validation rules (e.g. uniqueness) can’t be expressed using <input> attributes, and in any case, if the user is an elite hacker they might simply delete the attributes using the browser’s devtools. To guard against these sorts of shenanigans, you should always use server-side validation.

src/lib/server/database.js 中,验证描述是否存在且唯一:

¥In src/lib/server/database.js, validate that the description exists and is unique:

src/lib/server/database
export function createTodo(userid, description) {
	if (description === '') {
		throw new Error('todo must have a description');
	}

	const todos = db.get(userid);

	if (todos.find((todo) => todo.description === description)) {
		throw new Error('todos must be unique');
	}

	todos.push({
		id: crypto.randomUUID(),
		description,
		done: false
	});
}

尝试提交重复的待办事项。哎呀!SvelteKit 将我们带到一个看起来不友好的错误页面。在服务器上,我们看到 ‘待办事项必须是唯一的’ 错误,但 SvelteKit 会向用户隐藏意外的错误消息,因为它们通常包含敏感数据。

¥Try submitting a duplicate todo. Yikes! SvelteKit takes us to an unfriendly-looking error page. On the server, we see a ‘todos must be unique’ error, but SvelteKit hides unexpected error messages from users because they often contain sensitive data.

最好停留在同一页面上,并提供出错的指示,以便用户可以修复它。为此,我们可以使用 fail 函数从操作中返回数据以及适当的 HTTP 状态代码:

¥It would be much better to stay on the same page and provide an indication of what went wrong so that the user can fix it. To do this, we can use the fail function to return data from the action along with an appropriate HTTP status code:

src/routes/+page.server
import { fail } from '@sveltejs/kit';
import * as db from '$lib/server/database.js';

export function load({ cookies }) {...}

export const actions = {
	create: async ({ cookies, request }) => {
		const data = await request.formData();

		try {
			db.createTodo(cookies.get('userid'), data.get('description'));
		} catch (error) {
			return fail(422, {
				description: data.get('description'),
				error: error.message
			});
		}
	}

src/routes/+page.svelte 中,我们可以通过 form 属性访问返回值,该属性仅在表单提交后填充:

¥In src/routes/+page.svelte, we can access the returned value via the form prop, which is only ever populated after a form submission:

src/routes/+page
<script>
	let { data, form } = $props();
</script>

<div class="centered">
	<h1>todos</h1>

	{#if form?.error}
		<p class="error">{form.error}</p>
	{/if}

	<form method="POST" action="?/create">
		<label>
			add a todo:
			<input
				name="description"
				value={form?.description ?? ''}
				autocomplete="off"
				required
			/>
		</label>
	</form>
<script lang="ts">
	let { data, form } = $props();
</script>

<div class="centered">
	<h1>todos</h1>

	{#if form?.error}
		<p class="error">{form.error}</p>
	{/if}

	<form method="POST" action="?/create">
		<label>
			add a todo:
			<input
				name="description"
				value={form?.description ?? ''}
				autocomplete="off"
				required
			/>
		</label>
	</form>

你还可以从操作返回数据而不将其封装在 fail 中 - 例如在保存数据时显示 ‘成功!’ 消息 - 并且它将通过 form prop 提供。

¥[!NOTE] You can also return data from an action without wrapping it in fail — for example to show a ‘success!’ message when data was saved — and it will be available via the form prop.

上一页 下一页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<script>
	let { data } = $props();
</script>
 
<div class="centered">
	<h1>todos</h1>
 
	<form method="POST" action="?/create">
		<label>
			add a todo:
			<input
				name="description"
				autocomplete="off"
			/>
		</label>
	</form>
 
	<ul class="todos">
		{#each data.todos as todo (todo.id)}
			<li>
				<form method="POST" action="?/delete">
					<input type="hidden" name="id" value={todo.id} />
					<span>{todo.description}</span>
					<button aria-label="Mark as complete"></button>
				</form>
			</li>
		{/each}
	</ul>
</div>
 
<style>
	.centered {
		max-width: 20em;
		margin: 0 auto;
	}
 
	label {
		width: 100%;
	}
 
	input {
		flex: 1;
	}
 
	span {
		flex: 1;
	}
 
	button {
		border: none;
		background: url(./remove.svg) no-repeat 50% 50%;
		background-size: 1rem 1rem;
		cursor: pointer;
		height: 100%;
		aspect-ratio: 1;
		opacity: 0.5;
		transition: opacity 0.2s;
	}
 
	button:hover {
		opacity: 1;
	}
 
	.saving {
		opacity: 0.5;
	}
</style>