全书目录

第三十四篇:Hono 完全指南 —— 边缘轻骑哨站宇宙

第七站:校验就是海关,不要让脏数据进城

2 分钟 596 字 第 643 / 962 个阅读单元

Hono 自带的 validator 很薄,这恰恰符合它的风格:框架不替你绑死一种校验哲学,但给你一个很好接入第三方的关口。

在 2026 这个时间点,常见做法主要有两条:

  • @hono/zod-validator
  • @hono/standard-validator + Zod / Valibot / ArkType

如果你想最快上手,先用 Zod。

#最常见写法:zValidator

ts
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 官方文档专门提醒过这一点:

如果你校验的是 jsonform,请求必须带匹配的 content-type,否则解析不到,回调里拿到的很可能是空对象。

比如测试时这样是错的:

ts
const res = await app.request('/outposts', {
  method: 'POST',
  body: JSON.stringify({ name: '北境一号', zone: 'north', capacity: 30 }),
})

正确写法要带头:

ts
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 项目是很有吸引力的。