Hono 自带的 validator 很薄,这恰恰符合它的风格:框架不替你绑死一种校验哲学,但给你一个很好接入第三方的关口。
在 2026 这个时间点,常见做法主要有两条:
@hono/zod-validator@hono/standard-validator+ Zod / Valibot / ArkType
如果你想最快上手,先用 Zod。
#最常见写法:zValidator
import { Hono } from 'hono'
import { z } from 'zod'
import { zValidator } from '@hono/zod-validator'
const app = new Hono()
const createOutpostSchema = z.object({
name: z.string().min(1),
zone: z.enum(['north', 'south', 'east', 'west']),
capacity: z.coerce.number().int().positive(),
})
app.post(
'/outposts',
zValidator('json', createOutpostSchema),
(c) => {
const input = c.req.valid('json')
return c.json(
{
id: 'north-1',
...input,
},
201
)
}
)这里的心智非常清楚:
zValidator('json', schema):海关查验这份入境文书c.req.valid('json'):拿到已经过关的干净数据- 后面的 handler 就别再怀疑人生了,直接处理业务
#一个高频坑:忘了 Content-Type
Hono 官方文档专门提醒过这一点:
如果你校验的是 json 或 form,请求必须带匹配的 content-type,否则解析不到,回调里拿到的很可能是空对象。
比如测试时这样是错的:
const res = await app.request('/outposts', {
method: 'POST',
body: JSON.stringify({ name: '北境一号', zone: 'north', capacity: 30 }),
})正确写法要带头:
const res = await app.request('/outposts', {
method: 'POST',
body: JSON.stringify({ name: '北境一号', zone: 'north', capacity: 30 }),
headers: {
'Content-Type': 'application/json',
},
})这个坑本质上不是 Hono 在刁难你,而是它更尊重标准请求语义。
#Standard Schema 的价值
如果你不想把项目心智完全绑定在 Zod 上,可以考虑 @hono/standard-validator。
它的意思是:Hono 希望“海关接口”统一,但允许你换不同校验官。
这对中大型 TypeScript 项目是很有吸引力的。