验证数据
¥Validate Data
在处理数据之前,请确保数据有效且安全。
¥Ensure that your data are valid and safe before processing them.
当你在服务器上接收数据时,必须对其进行验证。我们所说的验证是指接收数据的形状必须与预期形状匹配。这很重要,因为你不能信任来自未知来源(例如用户或外部 API)的数据。
¥When you receive data on your server, you must validate them. By validate, we mean that the shape of the received data must match the expected shape. It's important because you can't trust the data coming from unknown sources, like a user or an external API.
readBody
这样的实用程序的接口并非验证。你必须在使用数据之前验证数据。¥!WARNING
Do not use type generics as a validation. Providing an interface to a utility like readBody
is not a validation. You must validate the data before using it.验证实用程序
¥Utilities for Validation
H3 提供了一些实用程序来帮助你处理数据验证。你将能够验证:
¥H3 provide some utilities to help you to handle data validation. You will be able to validate:
- 查询部分包含
getValidatedQuery
- 参数部分包含
getValidatedRouterParams
。 - 主体部分包含
readValidatedBody
H3 不提供任何验证库,但它支持来自标准模式兼容库的模式,例如:Zod、Valibot、ArkType 等……(所有兼容库请勾选 其官方存储库)。如果你想使用与 Standard-Schema 不兼容的验证库,你仍然可以使用它,但你必须使用该库本身提供的解析函数(请参阅下面的 安全解析 部分)。
¥H3 doesn't provide any validation library but it does support schemas coming from a Standard-Schema compatible one, like: Zod, Valibot, ArkType, etc... (for all compatible libraries please check their official repository). If you want to use a validation library that is not compatible with Standard-Schema, you can still use it, but you will have to use parsing functions provided by the library itself (refer to the Safe Parsing section below).
¥Let's see how to validate data with Zod and Valibot.
验证参数
¥Validate Params
你可以使用 getValidatedRouterParams
验证参数并获取结果,以替代 getRouterParams
:
¥You can use getValidatedRouterParams
to validate params and get the result, as a replacement of getRouterParams
:
import { getValidatedRouterParams } from "h3";
import * as z from "zod";
import * as v from "valibot";
// Example with Zod
const contentSchema = z.object({
topic: z.string().min(1),
uuid: z.string().uuid(),
});
// Example with Valibot
const contentSchema = v.object({
topic: v.pipe(v.string(), v.nonEmpty()),
uuid: v.pipe(v.string(), v.uuid()),
});
router.use(
// You must use a router to use params
"/content/:topic/:uuid",
async (event) => {
const params = await getValidatedRouterParams(event, contentSchema);
return `You are looking for content with topic "${params.topic}" and uuid "${params.uuid}".`;
},
);
如果你向此事件处理程序发送一个类似 /content/posts/123e4567-e89b-12d3-a456-426614174000
的有效请求,你将收到如下响应:
¥If you send a valid request like /content/posts/123e4567-e89b-12d3-a456-426614174000
to this event handler, you will get a response like this:
You are looking for content with topic "posts" and uuid "123e4567-e89b-12d3-a456-426614174000".
如果你发送了一个无效请求且验证失败,H3 将抛出 400 Validation Error
错误。在错误数据中,你将找到可在客户端上使用的验证错误,以便向用户显示友好的错误消息。
¥If you send an invalid request and the validation fails, H3 will throw a 400 Validation Error
error. In the data of the error, you will find the validation errors you can use on your client to display a nice error message to your user.
验证查询
¥Validate Query
你可以使用 getValidatedQuery
验证查询并获取结果,以替代 getQuery
:
¥You can use getValidatedQuery
to validate query and get the result, as a replacement of getQuery
:
import { getValidatedQuery } from "h3";
import * as z from "zod";
import * as v from "valibot";
// Example with Zod
const stringToNumber = z
.string()
.regex(/^\d+$/, "Must be a number string")
.transform(Number);
const paginationSchema = z.object({
page: stringToNumber.optional().default(1),
size: stringToNumber.optional().default(10),
});
// Example with Valibot
const stringToNumber = v.pipe(
v.string(),
v.regex(/^\d+$/, "Must be a number string"),
v.transform(Number),
);
const paginationSchema = v.object({
page: v.optional(stringToNumber, 1),
size: v.optional(stringToNumber, 10),
});
app.use(async (event) => {
const query = await getValidatedQuery(event, paginationSchema);
return `You are on page ${query.page} with ${query.size} items per page.`;
});
你可能已经注意到,与 getValidatedRouterParams
示例相比,我们可以利用验证库来转换传入的数据。在本例中,我们将数字的字符串表示形式转换为实数,这对于内容分页等功能非常有用。
¥As you may have noticed, compared to the getValidatedRouterParams
example, we can leverage validation libraries to transform the incoming data. In this case, we transform the string representation of a number into a real number, which is useful for things like content pagination.
如果你向此事件处理程序发送一个类似 /?page=2&size=20
的有效请求,你将收到如下响应:
¥If you send a valid request like /?page=2&size=20
to this event handler, you will get a response like this:
You are on page 2 with 20 items per page.
如果你发送了一个无效请求且验证失败,H3 将抛出 400 Validation Error
错误。在错误数据中,你将找到可在客户端上使用的验证错误,以便向用户显示友好的错误消息。
¥If you send an invalid request and the validation fails, H3 will throw a 400 Validation Error
error. In the data of the error, you will find the validation errors you can use on your client to display a nice error message to your user.
验证主体
¥Validate Body
你可以使用 readValidatedBody
验证正文并获取结果,以替代 readBody
:
¥You can use readValidatedBody
to validate body and get the result, as a replacement of readBody
:
import { readValidatedBody } from "h3";
import { z } from "zod";
import * as v from "valibot";
// Example with Zod
const userSchema = z.object({
name: z.string().min(3).max(20),
age: z.number({ coerce: true }).positive().int(),
});
// Example with Valibot
const userSchema = v.object({
name: v.pipe(v.string(), v.minLength(3), v.maxLength(20)),
age: v.pipe(v.number(), v.integer(), v.minValue(0)),
});
app.use(async (event) => {
const body = await readValidatedBody(event, userSchema);
return `Hello ${body.name}! You are ${body.age} years old.`;
});
如果你发送一个包含 JSON 格式的有效 POST 请求,如下所示:
¥If you send a valid POST request with a JSON body like this:
{
"name": "John",
"age": 42
}
你将收到如下响应:
¥You will get a response like this:
Hello John! You are 42 years old.
如果你发送了一个无效请求且验证失败,H3 将抛出 400 Validation Error
错误。在错误数据中,你将找到可在客户端上使用的验证错误,以便向用户显示友好的错误消息。
¥If you send an invalid request and the validation fails, H3 will throw a 400 Validation Error
error. In the data of the error, you will find the validation errors you can use on your client to display a nice error message to your user.
安全解析
¥Safe Parsing
默认情况下,如果每个验证工具(getValidatedRouterParams
、getValidatedQuery
和 readValidatedBody
)的第二个参数直接提供 schema,则验证失败时会抛出 400 Validation Error
错误,但在某些情况下,你可能需要自行处理验证错误。为此,你应该根据所使用的验证库,将实际的安全验证函数作为第二个参数提供。
¥By default if a schema is directly provided as e second argument for each validation utility (getValidatedRouterParams
, getValidatedQuery
, and readValidatedBody
) it will throw a 400 Validation Error
error if the validation fails, but in some cases you may want to handle the validation errors yourself. For this you should provide the actual safe validation function as the second argument, depending on the validation library you are using.
回到第一个使用 getValidatedRouterParams
的示例,对于 Zod 来说,它看起来像这样:
¥Going back to the first example with getValidatedRouterParams
, for Zod it would look like this:
import { getValidatedRouterParams } from "h3";
import { z } from "zod/v4";
const contentSchema = z.object({
topic: z.string().min(1),
uuid: z.string().uuid(),
});
router.use("/content/:topic/:uuid", async (event) => {
const params = await getValidatedRouterParams(event, contentSchema.safeParse);
if (!params.success) {
// Handle validation errors
return `Validation failed:\n${z.prettifyError(params.error)}`;
}
return `You are looking for content with topic "${params.data.topic}" and uuid "${params.data.uuid}".`;
});
对于 Valibot 来说,它看起来应该是这样的:
¥And for Valibot, it would look like this:
import { getValidatedRouterParams } from "h3";
import * as v from "valibot";
const contentSchema = v.object({
topic: v.pipe(v.string(), v.nonEmpty()),
uuid: v.pipe(v.string(), v.uuid()),
});
router.use("/content/:topic/:uuid", async (event) => {
const params = await getValidatedRouterParams(
event,
v.safeParser(contentSchema),
);
if (!params.success) {
// Handle validation errors
return `Validation failed:\n${v.summarize(params.issues)}`;
}
return `You are looking for content with topic "${params.output.topic}" and uuid "${params.output.uuid}".`;
});