验证
Hono 自带的验证器非常轻量,但结合第三方库后可以发挥强大作用。此外,RPC 功能还能通过类型将 API 规范共享给客户端。
手动验证器
首先介绍在不依赖第三方库的情况下验证输入的方法。
从 hono/validator 导入 validator:
ts
import { validator } from 'hono/validator'要验证表单数据,将 form 作为第一个参数传入,并提供一个回调函数作为第二个参数。在回调中执行验证并返回通过验证的数据。validator 可以作为中间件使用。
ts
app.post(
'/posts',
validator('form', (value, c) => {
const body = value['body']
if (!body || typeof body !== 'string') {
return c.text('Invalid!', 400)
}
return {
body: body,
}
}),
// ...在处理函数中,可通过 c.req.valid('form') 获取验证后的数据。
ts
(c) => {
const { body } = c.req.valid('form')
// ... do something
return c.json(
{
message: 'Created!',
},
201
)
}
)除 form 外,验证目标还包括 json、query、header、param 与 cookie。
WARNING
当验证 json 或 form 时,请求必须携带匹配的 content-type 头(例如 Content-Type: application/json)。否则请求体不会被解析,回调中会收到一个空对象 {}。
使用 app.request() 进行测试时,请务必设置 content-type。
假设应用如下:
ts
const app = new Hono()
app.post(
'/testing',
validator('json', (value, c) => {
// 透传验证器
return value
}),
(c) => {
const body = c.req.valid('json')
return c.json(body)
}
)测试用例如下:
ts
// ❌ 这样不会生效
const res = await app.request('/testing', {
method: 'POST',
body: JSON.stringify({ key: 'value' }),
})
const data = await res.json()
console.log(data) // {}
// ✅ 这样可以
const res = await app.request('/testing', {
method: 'POST',
body: JSON.stringify({ key: 'value' }),
headers: new Headers({ 'Content-Type': 'application/json' }),
})
const data = await res.json()
console.log(data) // { key: 'value' }WARNING
验证 header 时,必须使用小写作为键名。
例如要验证 Idempotency-Key,需要使用 idempotency-key 作为键。
ts
// ❌ 这样无法工作
app.post(
'/api',
validator('header', (value, c) => {
// idempotencyKey 始终为 undefined
// 因此中间件会意外返回 400
const idempotencyKey = value['Idempotency-Key']
if (idempotencyKey == undefined || idempotencyKey === '') {
throw new HTTPException(400, {
message: 'Idempotency-Key is required',
})
}
return { idempotencyKey }
}),
(c) => {
const { idempotencyKey } = c.req.valid('header')
// ...
}
)
// ✅ 使用小写键名
app.post(
'/api',
validator('header', (value, c) => {
// 可以正确读取请求头
const idempotencyKey = value['idempotency-key']
if (idempotencyKey == undefined || idempotencyKey === '') {
throw new HTTPException(400, {
message: 'Idempotency-Key is required',
})
}
return { idempotencyKey }
}),
(c) => {
const { idempotencyKey } = c.req.valid('header')
// ...
}
)多个验证器
你可以同时添加多个验证器,以验证请求的不同部分:
ts
app.post(
'/posts/:id',
validator('param', ...),
validator('query', ...),
validator('json', ...),
(c) => {
// ...
}
)搭配 Zod
可以使用第三方验证库 Zod,我们推荐使用这类库。
从 npm 安装:
sh
npm i zodsh
yarn add zodsh
pnpm add zodsh
bun add zod导入 z:
ts
import * as z from 'zod'编写模式:
ts
const schema = z.object({
body: z.string(),
})在回调函数中使用模式进行验证,并返回验证通过的值:
ts
const route = app.post(
'/posts',
validator('form', (value, c) => {
const parsed = schema.safeParse(value)
if (!parsed.success) {
return c.text('Invalid!', 401)
}
return parsed.data
}),
(c) => {
const { body } = c.req.valid('form')
// ... do something
return c.json(
{
message: 'Created!',
},
201
)
}
)Zod 验证器中间件
使用 Zod Validator 中间件 可以进一步简化:
sh
npm i @hono/zod-validatorsh
yarn add @hono/zod-validatorsh
pnpm add @hono/zod-validatorsh
bun add @hono/zod-validator导入 zValidator:
ts
import { zValidator } from '@hono/zod-validator'然后这样写:
ts
const route = app.post(
'/posts',
zValidator(
'form',
z.object({
body: z.string(),
})
),
(c) => {
const validated = c.req.valid('form')
// ... 使用验证后的数据
}
)