# 请求验证

URL: https://caijiao.org/hono/04-middleware/03-validator
Source: docs/hono/04-middleware/03-validator.md
Description: 学习 Hono Validator 与 @hono/zod-validator 的基本用法，掌握 JSON、查询参数和表单验证方式。

TypeScript 类型不能验证真实请求。公开 API 必须在运行时检查 JSON、查询参数、表单数据和路径参数。

Hono 自带轻量 `validator`，也常与 `@hono/zod-validator`、Zod 搭配使用。

## 使用 validator

```ts
import { validator } from 'hono/validator'

app.post(
  '/posts',
  validator('json', (value, c) => {
    const title = value['title']

    if (typeof title !== 'string' || title.trim() === '') {
      return c.json({ message: 'title is required' }, 400)
    }

    return {
      title: title.trim(),
    }
  }),
  (c) => {
    const input = c.req.valid('json')
    return c.json({ id: 1, title: input.title }, 201)
  },
)
```

验证通过后，通过 `c.req.valid('json')` 读取整理后的值。

## 使用 Zod Validator

先安装：

```bash
pnpm add zod @hono/zod-validator
```

定义 schema：

```ts
import { z } from 'zod'
import { zValidator } from '@hono/zod-validator'

const createPostSchema = z.object({
  title: z.string().min(1).max(100),
  body: z.string().min(1),
})

app.post('/posts', zValidator('json', createPostSchema), (c) => {
  const input = c.req.valid('json')
  return c.json({ id: crypto.randomUUID(), ...input }, 201)
})
```

## 验证查询参数

```ts
const listQuerySchema = z.object({
  page: z.coerce.number().int().positive().default(1),
  pageSize: z.coerce.number().int().min(1).max(100).default(20),
})

app.get('/posts', zValidator('query', listQuerySchema), (c) => {
  const query = c.req.valid('query')
  return c.json(query)
})
```

查询参数本来都是字符串，`z.coerce.number()` 可以把字符串转为数字。

## 验证表单

```ts
const contactSchema = z.object({
  email: z.string().email(),
  message: z.string().min(1).max(1000),
})

app.post('/contact', zValidator('form', contactSchema), (c) => {
  const form = c.req.valid('form')
  return c.json({ received: true, email: form.email })
})
```

## 验证建议

- 所有外部输入都要验证，包括请求体、查询参数、路径参数和 Header。
- 验证器返回的对象应尽量是业务代码真正需要的结构。
- 错误响应要稳定，方便前端展示和测试断言。
- 不要把数据库层错误当作输入验证。输入错误应该在进入业务逻辑之前处理。
